func (f *Function) SimdIntrinsic(call *ssa.Call) (string, *Error) { var asm string var err *Error args := call.Common().Args x := f.Ident(args[0]) y := f.Ident(args[1]) result := f.Ident(call) name := call.Common().StaticCallee().Name() if simdinstr, ok := getSimdInstr(name); ok { if result.typ != x.typ { panic(ice(fmt.Sprintf("Simd variable type (%v) and op type (%v) dont match", result.typ.String(), x.typ.String()))) } optypes := GetOpDataType(x.typ) a, e := packedOp(f, call, simdinstr, optypes.xmmvariant, y, x, result) asm = a err = e } else { intrinsic, ok := intrinsics[name] if !ok { panic(ice(fmt.Sprintf("Expected simd intrinsic got (%v)", name))) } a, e := intrinsic(f, call, x, y, result) asm = a err = e } asm = fmt.Sprintf("// BEGIN SIMD Intrinsic %v\n", call) + asm + fmt.Sprintf("// END SIMD Intrinsic %v\n", call) return asm, err }
// genAppend generates constraints for a call to append. func (a *analysis) genAppend(instr *ssa.Call, cgn *cgnode) { // Consider z = append(x, y). y is optional. // This may allocate a new [1]T array; call its object w. // We get the following constraints: // z = x // z = &w // *z = *y x := instr.Call.Args[0] z := instr a.copy(a.valueNode(z), a.valueNode(x), 1) // z = x if len(instr.Call.Args) == 1 { return // no allocation for z = append(x) or _ = append(x). } // TODO(adonovan): test append([]byte, ...string) []byte. y := instr.Call.Args[1] tArray := sliceToArray(instr.Call.Args[0].Type()) var w nodeid w = a.nextNode() a.addNodes(tArray, "append") a.endObject(w, cgn, instr) a.copyElems(cgn, tArray.Elem(), z, y) // *z = *y a.addressOf(instr.Type(), a.valueNode(z), w) // z = &w }
func (f *Function) Len(call *ssa.Call) (string, *Error) { asm := fmt.Sprintf("// BEGIN Builtin.Len: %v\n", call) callcommon := call.Common() arg := callcommon.Args[0] ctx := context{f, call} if callcommon.IsInvoke() { panic(ice("len is a function, not a method")) } if len(callcommon.Args) != 1 { panic(ice(fmt.Sprintf("too many args (%v) for len", len(callcommon.Args)))) } ident := f.Ident(call.Value()) if reflectType(ident.typ).Name() != "int" { panic(ice(fmt.Sprintf("len returns int not (%v)", reflectType(ident.typ).Name()))) } if isArray(arg.Type()) { length := reflectType(arg.Type()).Len() if length >= math.MaxInt32 { panic(ice(fmt.Sprintf("array too large (%v), maximum (%v)", length, math.MaxInt32))) } a, reg := f.allocIdentReg(call, ident, sizeof(ident.typ)) asm += a asm += MovImm32Reg(ctx, int32(length), reg, false) a, err := f.StoreValue(call, ident, reg) asm += a if err != nil { return asm, err } } else if isSlice(arg.Type()) { a, err := f.SliceLen(call, arg, ident) asm += a if err != nil { return asm, err } } else { panic(ice(fmt.Sprintf("bad type (%v) passed to len", arg.Type()))) } asm += fmt.Sprintf("// END Builtin.Len: %v\n", call) return asm, nil }
func isSSE2Intrinsic(call *ssa.Call) (Intrinsic, bool) { if call.Common() == nil || call.Common().StaticCallee() == nil { return Intrinsic{}, false } name := call.Common().StaticCallee().Name() return getSSE2(name) }
func isSimdIntrinsic(call *ssa.Call) bool { if call.Common() == nil || call.Common().StaticCallee() == nil { return false } name := call.Common().StaticCallee().Name() if _, ok := getSimdInstr(name); ok { return ok } else { _, ok := intrinsics[name] return ok } }
func (f *Function) SSE2Intrinsic(call *ssa.Call, sse2intrinsic Intrinsic) (string, *Error) { args := call.Common().Args return sse2Intrinsic(f, call, call, sse2intrinsic, args), nil }
func (f *Function) Call(call *ssa.Call) (string, *Error) { common := call.Common() funct := common.Value if builtin, ok := funct.(*ssa.Builtin); ok { return f.Builtin(call, builtin) } if isSimdIntrinsic(call) { return f.SimdIntrinsic(call) } if sse2instr, ok := isSSE2Intrinsic(call); ok { return f.SSE2Intrinsic(call, sse2instr) } name := "UNKNOWN FUNC NAME" if call.Common().Method != nil { name = call.Common().Method.Name() } else if call.Common().StaticCallee() != nil { name = call.Common().StaticCallee().Name() } msg := fmt.Sprintf("function calls are not supported, func name (%v), description (%v)", name, call.Common().Description()) return ErrorMsg(msg) }
func visitCall(instr *ssa.Call, infer *TypeInfer, ctx *Context) { infer.Logger.Printf(ctx.F.Sprintf(CallSymbol+"%s = %s", instr.Name(), instr.String())) ctx.F.Call(instr, infer, ctx.B, ctx.L) }
// Call performs call on a given unprepared call context. func (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block, l *Loop) { if call == nil { infer.Logger.Fatal("Call is nil") return } common := call.Common() switch fn := common.Value.(type) { case *ssa.Builtin: switch fn.Name() { case "close": ch, ok := caller.locals[common.Args[0]] if !ok { infer.Logger.Fatalf("call close: %s: %s", common.Args[0].Name(), ErrUnknownValue) return } if paramName, ok := caller.revlookup[ch.String()]; ok { caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: paramName}) } else { if _, ok := common.Args[0].(*ssa.Phi); ok { caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: common.Args[0].Name()}) } else { caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: ch.(*Value).Name()}) } } infer.Logger.Print(caller.Sprintf("close %s", common.Args[0])) return case "len": if l.State == Enter { len, err := caller.callLen(common, infer) if err == ErrRuntimeLen { l.Bound = Dynamic return } l.Bound, l.End = Static, len return } caller.locals[call] = &Value{call, caller.InstanceID(), l.Index} infer.Logger.Printf(caller.Sprintf(" builtin.%s", common.String())) default: infer.Logger.Printf(caller.Sprintf(" builtin.%s", common.String())) } case *ssa.MakeClosure: infer.Logger.Printf(caller.Sprintf(SkipSymbol+" make closure %s", fn.String())) caller.callClosure(common, fn, infer, b, l) case *ssa.Function: if common.StaticCallee() == nil { infer.Logger.Fatal("Call with nil CallCommon") } callee := caller.callFn(common, infer, b, l) if callee != nil { caller.storeRetvals(infer, call.Value(), callee) } default: if !common.IsInvoke() { infer.Logger.Print("Unknown call type", common.String(), common.Description()) return } callee := caller.invoke(common, infer, b, l) if callee != nil { caller.storeRetvals(infer, call.Value(), callee) } else { // Mock out the return values. switch common.Signature().Results().Len() { case 0: case 1: caller.locals[call.Value()] = &External{ parent: caller.Fn, typ: call.Value().Type().Underlying(), } case 2: caller.locals[call.Value()] = &External{typ: call.Value().Type().Underlying()} caller.tuples[caller.locals[call.Value()]] = make(Tuples, common.Signature().Results().Len()) } } } }
func (caller *frame) callCommon(call *ssa.Call, common *ssa.CallCommon) { switch fn := common.Value.(type) { case *ssa.Builtin: caller.callBuiltin(common) case *ssa.MakeClosure: // TODO(nickng) Handle calling closure fmt.Fprintf(os.Stderr, " # TODO (handle closure) %s\n", fn.String()) case *ssa.Function: if common.StaticCallee() == nil { panic("Call with nil CallCommon!") } callee := &frame{ fn: common.StaticCallee(), locals: make(map[ssa.Value]*utils.Definition), arrays: make(map[*utils.Definition]Elems), structs: make(map[*utils.Definition]Fields), tuples: make(map[ssa.Value]Tuples), phi: make(map[ssa.Value][]ssa.Value), recvok: make(map[ssa.Value]*sesstype.Chan), retvals: make(Tuples, common.Signature().Results().Len()), defers: make([]*ssa.Defer, 0), caller: caller, env: caller.env, // Use the same env as caller gortn: caller.gortn, // Use the same role as caller } fmt.Fprintf(os.Stderr, "++ call %s(", orange(common.StaticCallee().String())) callee.translate(common) fmt.Fprintf(os.Stderr, ")\n") if callee.isRecursive() { fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(common.StaticCallee().String())) callee.printCallStack() } else { if hasCode := visitFunc(callee.fn, callee); hasCode { caller.handleRetvals(call.Value(), callee) } else { caller.handleExtRetvals(call.Value(), callee) } fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(common.StaticCallee().String()), len(callee.retvals)) } default: if !common.IsInvoke() { fmt.Fprintf(os.Stderr, "Unknown call type %v\n", common) return } switch vd, kind := caller.get(common.Value); kind { case Struct, LocalStruct: fmt.Fprintf(os.Stderr, "++ invoke %s.%s, type=%s\n", reg(common.Value), common.Method.String(), vd.Var.Type().String()) // If dealing with interfaces, check that the method is invokable if iface, ok := common.Value.Type().Underlying().(*types.Interface); ok { if meth, _ := types.MissingMethod(vd.Var.Type(), iface, true); meth != nil { fmt.Fprintf(os.Stderr, " ^ interface not fully implemented\n") } else { fn := findMethod(common.Value.Parent().Prog, common.Method, vd.Var.Type()) if fn != nil { fmt.Fprintf(os.Stderr, " ^ found function %s\n", fn.String()) callee := &frame{ fn: fn, locals: make(map[ssa.Value]*utils.Definition), arrays: make(map[*utils.Definition]Elems), structs: make(map[*utils.Definition]Fields), tuples: make(map[ssa.Value]Tuples), phi: make(map[ssa.Value][]ssa.Value), recvok: make(map[ssa.Value]*sesstype.Chan), retvals: make(Tuples, common.Signature().Results().Len()), defers: make([]*ssa.Defer, 0), caller: caller, env: caller.env, // Use the same env as caller gortn: caller.gortn, // Use the same role as caller } common.Args = append([]ssa.Value{common.Value}, common.Args...) fmt.Fprintf(os.Stderr, "++ call %s(", orange(fn.String())) callee.translate(common) fmt.Fprintf(os.Stderr, ")\n") if callee.isRecursive() { fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(fn.String())) callee.printCallStack() } else { if hasCode := visitFunc(callee.fn, callee); hasCode { caller.handleRetvals(call.Value(), callee) } else { caller.handleExtRetvals(call.Value(), callee) } fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(fn.String()), len(callee.retvals)) } } else { panic(fmt.Sprintf("Cannot call function: %s.%s is abstract (program not well-formed)", common.Value, common.Method.String())) } } } else { fmt.Fprintf(os.Stderr, " ^ method %s.%s does not exist\n", reg(common.Value), common.Method.String()) } default: fmt.Fprintf(os.Stderr, "++ invoke %s.%s\n", reg(common.Value), common.Method.String()) } } }
func (caller *frame) call(c *ssa.Call) { caller.callCommon(c, c.Common()) }