// If this value is sufficiently large, look through referrers to see if we can // avoid a load. func (fr *frame) canAvoidLoad(instr *ssa.UnOp, op llvm.Value) bool { if fr.types.Sizeof(instr.Type()) < 16 { // Don't bother with small values. return false } // Keep track of whether our pointer may escape. We conservatively assume // that MakeInterfaces will escape. esc := false // We only know how to avoid loads if they are used to create an interface // or read an element of the structure. If we see any other referrer, abort. for _, ref := range *instr.Referrers() { switch ref.(type) { case *ssa.MakeInterface: esc = true case *ssa.Field, *ssa.Index: // ok default: return false } } var opcopy llvm.Value if esc { opcopy = fr.createTypeMalloc(instr.Type()) } else { opcopy = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(instr.Type()), "") } fr.memcpy(opcopy, op, llvm.ConstInt(fr.types.inttype, uint64(fr.types.Sizeof(instr.Type())), false)) fr.ptr[instr] = opcopy return true }
func visitDeref(inst *ssa.UnOp, fr *frame) { ptr := inst.X val := inst if _, ok := ptr.(*ssa.Global); ok { fr.locals[ptr] = fr.env.globals[ptr] fmt.Fprintf(os.Stderr, " %s = *%s (global) of type %s\n", cyan(reg(val)), ptr.Name(), ptr.Type().String()) fmt.Fprintf(os.Stderr, " ^ i.e. %s\n", fr.locals[ptr].String()) switch deref(fr.locals[ptr].Var.Type()).(type) { case *types.Array, *types.Slice: if _, ok := fr.env.arrays[fr.env.globals[ptr]]; !ok { fr.env.arrays[fr.env.globals[ptr]] = make(Elems) } case *types.Struct: if _, ok := fr.env.structs[fr.env.globals[ptr]]; !ok { fr.env.structs[fr.env.globals[ptr]] = make(Fields) } } } switch vd, kind := fr.get(ptr); kind { case Array, LocalArray: fr.locals[val] = vd fmt.Fprintf(os.Stderr, " %s = *%s (array)\n", cyan(reg(val)), ptr.Name()) case Struct, LocalStruct: fr.locals[val] = vd fmt.Fprintf(os.Stderr, " %s = *%s (struct)\n", cyan(reg(val)), ptr.Name()) case Chan: fr.locals[val] = vd fmt.Fprintf(os.Stderr, " %s = *%s (previously initalised Chan)\n", cyan(reg(val)), ptr.Name()) case Nothing: fmt.Fprintf(os.Stderr, " # %s = *%s (not found)\n", red(inst.String()), red(inst.X.String())) if _, ok := val.Type().Underlying().(*types.Chan); ok { fmt.Fprintf(os.Stderr, " ^ channel (not allocated, must be initialised by MakeChan)") } default: fmt.Fprintf(os.Stderr, " # %s = *%s/%s (not found, type=%s)\n", red(inst.String()), red(inst.X.String()), reg(inst.X), inst.Type().String()) } }
func (f *Function) UnOp(instr *ssa.UnOp) (string, *Error) { var err *Error asm := "" switch instr.Op { default: ice(fmt.Sprintf("unknown Op token (%v): \"%v\"", instr.Op, instr)) case token.NOT: // logical negation asm, err = f.UnOpXor(instr, 1) case token.XOR: //bitwise negation asm, err = f.UnOpXor(instr, -1) case token.SUB: // arithmetic negation e.g. x=>-x asm, err = f.UnOpSub(instr) case token.MUL: //pointer indirection asm, err = f.UnOpPointer(instr) } asm = fmt.Sprintf("// BEGIN ssa.UnOp: %v = %v\n", instr.Name(), instr) + asm asm += fmt.Sprintf("// END ssa.UnOp: %v = %v\n", instr.Name(), instr) return asm, err }
// bitwise negation func (f *Function) UnOpXor(instr *ssa.UnOp, xorVal int32) (string, *Error) { ctx := context{f, instr} if ident := f.Ident(instr); ident == nil { return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr)) } addr, ok := f.identifiers[instr.Name()] if !ok { msg := fmt.Sprintf("unknown name (%v), instr (%v)\n", instr.Name(), instr) ice(msg) } asm, reg, err := f.LoadValueSimple(instr, instr.X) if err != nil { return asm, err } if reg.size() < 8 { asm += XorImm32Reg(ctx, xorVal, reg, reg.size(), true) } else { asm += XorImm64Reg(ctx, int64(xorVal), reg, reg.size(), true) } a, err := f.StoreValue(instr, addr, reg) f.freeReg(reg) if err != nil { return asm, err } else { asm += a } asm = fmt.Sprintf("// BEGIN ssa.UnOpNot, %v = %v\n", instr.Name(), instr) + asm asm += fmt.Sprintf("// END ssa.UnOpNot, %v = %v\n", instr.Name(), instr) return asm, nil }
//pointer indirection, in assignment such as "z = *x" func (f *Function) UnOpPointer(instr *ssa.UnOp) (string, *Error) { asm := "" assignment := f.Ident(instr) if assignment == nil { return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr)) } xName := instr.X.Name() xInfo, okX := f.identifiers[xName] if !xInfo.isSsaLocal() && xInfo.param == nil && xInfo.ptr == nil { panic("unexpected nil ptr") } else if !xInfo.isSsaLocal() && xInfo.param == nil { asm += xInfo.ptr.spillAllRegisters(instr) } // TODO add complex64/128 support if isComplex(instr.Type()) || isComplex(instr.X.Type()) { return ErrorMsg("complex64/complex128 unimplemented") } if !okX { msgstr := "Unknown name for UnOp X (%v), instr \"(%v)\"" ice(fmt.Sprintf(msgstr, instr.X, instr)) } if xInfo.local == nil && xInfo.param == nil && !xInfo.isPointer() { fmtstr := "in UnOp, X (%v) isn't a pointer, X.type (%v), instr \"(%v)\"" msg := fmt.Sprintf(fmtstr, instr.X, instr.X.Type(), instr) ice(msg) } _, _, size := assignment.Addr() if xInfo.isSsaLocal() { ctx := context{f, instr} a, reg := xInfo.load(ctx) asm += a asm += assignment.newValue(ctx, reg, 0, xInfo.size()) f.freeReg(reg) } else { var tmpData *register var a1 string if isXmm(instr.Type()) { a1, tmpData = f.allocReg(instr, XMM_REG, XmmRegSize) } else { a1, tmpData = f.allocReg(instr, regType(instr.Type()), DataRegSize) } asm += a1 var a2 string var tmpAddr *register a2, tmpAddr = f.allocReg(instr, DATA_REG, DataRegSize) asm += a2 src, ok := xInfo.storage.(*memory) if !ok { ice("cannot dereference pointer to constant") } dst, ok := assignment.storage.(*memory) if !ok { ice("cannot modify constant") } dst.removeAliases() ctx := context{f, instr} a, srcReg := src.load(ctx, src.ownerRegion()) asm += a aReg, aOffset, _ := assignment.Addr() asm += MovRegIndirectMem(ctx, dst.optype(), srcReg, assignment.name, aOffset, &aReg, size, tmpAddr, tmpData) dst.setInitialized(region{0, size}) f.freeReg(srcReg) f.freeReg(tmpAddr) f.freeReg(tmpData) } asm = fmt.Sprintf("// BEGIN ssa.UnOpPointer, %v = %v\n", instr.Name(), instr) + asm asm += fmt.Sprintf("// END ssa.UnOpPointer, %v = %v\n", instr.Name(), instr) return asm, nil }
// arithmetic negation func (f *Function) UnOpSub(instr *ssa.UnOp) (string, *Error) { ctx := context{f, instr} if ident := f.Ident(instr); ident == nil { return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr)) } var regSubX, regX, regVal *register var a1, a2 string a1, regVal = f.allocReg(instr, regType(instr.Type()), f.sizeof(instr)) a2, regSubX = f.allocReg(instr, regType(instr.X.Type()), f.sizeof(instr.X)) addr, ok := f.identifiers[instr.Name()] if !ok { msg := fmt.Sprintf("unknown name (%v), instr (%v)\n", instr.Name(), instr) ice(msg) } a3, regX, err := f.LoadValueSimple(instr, instr.X) asm := a1 + a2 + a3 if err != nil { return asm, err } asm += ZeroReg(ctx, regSubX) optypes := GetOpDataType(instr.Type()) asm += ArithOp(ctx, optypes, token.SUB, regSubX, regX, regVal) f.freeReg(regX) f.freeReg(regSubX) a, err := f.StoreValue(instr, addr, regVal) if err != nil { return asm, err } else { asm += a } f.freeReg(regVal) asm = fmt.Sprintf("// BEGIN ssa.UnOpSub, %v = %v\n", instr.Name(), instr) + asm asm += fmt.Sprintf("// END ssa.UnOpSub, %v = %v\n", instr.Name(), instr) return asm, nil }