// makeSlice allocates a new slice with the optional length and capacity, // initialising its contents to their zero values. func (c *compiler) makeSlice(elttyp types.Type, length, capacity Value) llvm.Value { var lengthValue llvm.Value if length != nil { lengthValue = length.Convert(types.Typ[types.Int]).LLVMValue() } else { lengthValue = llvm.ConstNull(c.llvmtypes.inttype) } // TODO check capacity >= length capacityValue := lengthValue if capacity != nil { capacityValue = capacity.Convert(types.Typ[types.Int]).LLVMValue() } eltType := c.types.ToLLVM(elttyp) sizeof := llvm.ConstTruncOrBitCast(llvm.SizeOf(eltType), c.types.inttype) size := c.builder.CreateMul(capacityValue, sizeof, "") mem := c.createMalloc(size) mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "") c.memsetZero(mem, size) slicetyp := types.NewSlice(elttyp) struct_ := llvm.Undef(c.types.ToLLVM(slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, lengthValue, 1, "") struct_ = c.builder.CreateInsertValue(struct_, capacityValue, 2, "") return struct_ }
func (c *compiler) slice(x, low, high *LLVMValue) *LLVMValue { if low != nil { low = low.Convert(types.Typ[types.Int]).(*LLVMValue) } else { low = c.NewValue(llvm.ConstNull(c.types.inttype), types.Typ[types.Int]) } if high != nil { high = high.Convert(types.Typ[types.Int]).(*LLVMValue) } else { // all bits set is -1 high = c.NewValue(llvm.ConstAllOnes(c.types.inttype), types.Typ[types.Int]) } switch typ := x.Type().Underlying().(type) { case *types.Pointer: // *array sliceslice := c.runtime.sliceslice.LLVMValue() i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := llvm.Undef(i8slice) // temporary slice arraytyp := typ.Elem().Underlying().(*types.Array) arrayptr := x.LLVMValue() arrayptr = c.builder.CreateBitCast(arrayptr, i8slice.StructElementTypes()[0], "") arraylen := llvm.ConstInt(c.llvmtypes.inttype, uint64(arraytyp.Len()), false) sliceValue = c.builder.CreateInsertValue(sliceValue, arrayptr, 0, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 1, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 2, "") sliceTyp := types.NewSlice(arraytyp.Elem()) runtimeTyp := c.types.ToRuntime(sliceTyp) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low.LLVMValue(), high.LLVMValue()} result := c.builder.CreateCall(sliceslice, args, "") llvmSliceTyp := c.types.ToLLVM(sliceTyp) return c.NewValue(c.coerceSlice(result, llvmSliceTyp), sliceTyp) case *types.Slice: sliceslice := c.runtime.sliceslice.LLVMValue() i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := x.LLVMValue() sliceTyp := sliceValue.Type() sliceValue = c.coerceSlice(sliceValue, i8slice) runtimeTyp := c.types.ToRuntime(x.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low.LLVMValue(), high.LLVMValue()} result := c.builder.CreateCall(sliceslice, args, "") return c.NewValue(c.coerceSlice(result, sliceTyp), x.Type()) case *types.Basic: stringslice := c.runtime.stringslice.LLVMValue() llv := x.LLVMValue() args := []llvm.Value{ c.coerceString(llv, stringslice.Type().ElementType().ParamTypes()[0]), low.LLVMValue(), high.LLVMValue(), } result := c.builder.CreateCall(stringslice, args, "") return c.NewValue(c.coerceString(result, llv.Type()), x.Type()) default: panic("unimplemented") } panic("unreachable") }
func (tm *LLVMTypeMap) funcLLVMType(tstr string, f *types.Signature) llvm.Type { typ, ok := tm.types[tstr] if !ok { // If there's a receiver change the receiver to an // additional (first) parameter, and take the value of // the resulting signature instead. var param_types []llvm.Type if recv := f.Recv(); recv != nil { params := f.Params() paramvars := make([]*types.Var, int(params.Len()+1)) paramvars[0] = recv for i := 0; i < int(params.Len()); i++ { paramvars[i+1] = params.At(i) } params = types.NewTuple(paramvars...) f := types.NewSignature(nil, params, f.Results(), f.IsVariadic()) return tm.ToLLVM(f) } typ = llvm.GlobalContext().StructCreateNamed("") tm.types[tstr] = typ params := f.Params() nparams := int(params.Len()) for i := 0; i < nparams; i++ { typ := params.At(i).Type() if f.IsVariadic() && i == nparams-1 { typ = types.NewSlice(typ) } llvmtyp := tm.ToLLVM(typ) param_types = append(param_types, llvmtyp) } var return_type llvm.Type results := f.Results() switch nresults := int(results.Len()); nresults { case 0: return_type = llvm.VoidType() case 1: return_type = tm.ToLLVM(results.At(0).Type()) default: elements := make([]llvm.Type, nresults) for i := range elements { result := results.At(i) elements[i] = tm.ToLLVM(result.Type()) } return_type = llvm.StructType(elements, false) } fntyp := llvm.FunctionType(return_type, param_types, false) fnptrtyp := llvm.PointerType(fntyp, 0) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) elements := []llvm.Type{fnptrtyp, i8ptr} // func, closure typ.StructSetBody(elements, false) } return typ }
// generate generates offline constraints for the entire program. func (a *analysis) generate() { start("Constraint generation") if a.log != nil { fmt.Fprintln(a.log, "==== Generating constraints") } // Create a dummy node since we use the nodeid 0 for // non-pointerlike variables. a.addNodes(tInvalid, "(zero)") // Create the global node for panic values. a.panicNode = a.addNodes(tEface, "panic") // Create nodes and constraints for all methods of reflect.rtype. // (Shared contours are used by dynamic calls to reflect.Type // methods---typically just String().) if rtype := a.reflectRtypePtr; rtype != nil { a.genMethodsOf(rtype) } root := a.genRootCalls() if a.config.BuildCallGraph { a.result.CallGraph = callgraph.New(root.fn) } // Create nodes and constraints for all methods of all types // that are dynamically accessible via reflection or interfaces. for _, T := range a.prog.TypesWithMethodSets() { a.genMethodsOf(T) } // Generate constraints for entire program. for len(a.genq) > 0 { cgn := a.genq[0] a.genq = a.genq[1:] a.genFunc(cgn) } // The runtime magically allocates os.Args; so should we. if os := a.prog.ImportedPackage("os"); os != nil { // In effect: os.Args = new([1]string)[:] T := types.NewSlice(types.Typ[types.String]) obj := a.addNodes(sliceToArray(T), "<command-line args>") a.endObject(obj, nil, "<command-line args>") a.addressOf(T, a.objectNode(nil, os.Var("Args")), obj) } // Discard generation state, to avoid confusion after node renumbering. a.panicNode = 0 a.globalval = nil a.localval = nil a.localobj = nil stop("Constraint generation") }
// createParams creates parameters for wrapper method fn based on its // Signature.Params, which do not include the receiver. // func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() for i, n := 0, tparams.Len(); i < n; i++ { last = fn.addParamObj(tparams.At(i)) } if fn.Signature.Variadic() { last.typ = types.NewSlice(last.typ) } }
func (v *LLVMValue) stringToRuneSlice() *LLVMValue { c := v.compiler strtorunes := c.NamedFunction("runtime.strtorunes", "func(_string) slice") _string := strtorunes.Type().ElementType().ParamTypes()[0] args := []llvm.Value{c.coerceString(v.LLVMValue(), _string)} result := c.builder.CreateCall(strtorunes, args, "") runeslice := types.NewSlice(types.Typ[types.Rune]) result = c.coerceSlice(result, c.types.ToLLVM(runeslice)) return c.NewValue(result, runeslice) }
// ArrayOrSliceType = "[" [ int ] "]" Type . func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type { p.expect('[') if p.tok == ']' { p.next() return types.NewSlice(p.parseType(pkg)) } n := p.parseInt() p.expect(']') return types.NewArray(p.parseType(pkg), n) }
func (tm *TypeMap) arrayRuntimeType(a *types.Array) (global, ptr llvm.Value) { rtype := tm.makeRtype(a, reflect.Array) elemRuntimeType := tm.ToRuntime(a.Elem()) sliceRuntimeType := tm.ToRuntime(types.NewSlice(a.Elem())) uintptrlen := llvm.ConstInt(tm.target.IntPtrType(), uint64(a.Len()), false) arrayType := llvm.ConstNull(tm.runtime.arrayType.llvm) arrayType = llvm.ConstInsertValue(arrayType, rtype, []uint32{0}) arrayType = llvm.ConstInsertValue(arrayType, elemRuntimeType, []uint32{1}) arrayType = llvm.ConstInsertValue(arrayType, sliceRuntimeType, []uint32{2}) arrayType = llvm.ConstInsertValue(arrayType, uintptrlen, []uint32{3}) return tm.makeRuntimeTypeGlobal(arrayType, typeString(a)) }
func (c *reflectSliceOfConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for tObj := range delta { T := a.rtypeTaggedValue(tObj) if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) { changed = true } } if changed { a.addWork(c.result) } }
// createParams creates parameters for bridge method fn based on its Signature. func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() for i, n := 0, tparams.Len(); i < n; i++ { p := tparams.At(i) name := p.Name() if name == "" { name = fmt.Sprintf("arg%d", i) } last = fn.addParam(name, p.Type()) } if fn.Signature.IsVariadic() { last.Type_ = types.NewSlice(last.Type_) } }
// ArrayOrSliceType = "[" [ int ] "]" Type . func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type { p.expect('[') if p.tok == ']' { p.next() return types.NewSlice(p.parseType(pkg)) } lit := p.expect(scanner.Int) n, err := strconv.ParseInt(lit, 10, 0) if err != nil { p.error(err) } p.expect(']') return types.NewArray(p.parseType(pkg), n) }
// Param = Name ["..."] Type . func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) { name := p.parseName() if p.tok == '.' { p.next() p.expect('.') p.expect('.') isVariadic = true } typ := p.parseType(pkg) if isVariadic { typ = types.NewSlice(typ) } param = types.NewParam(token.NoPos, pkg, name, typ) return }
// Type = // BasicType | TypeName | ArrayType | SliceType | StructType | // PointerType | FuncType | InterfaceType | MapType | ChanType | // "(" Type ")" . // // BasicType = ident . // TypeName = ExportedName . // SliceType = "[" "]" Type . // PointerType = "*" Type . // FuncType = "func" Signature . // func (p *parser) parseType() types.Type { switch p.tok { case scanner.Ident: switch p.lit { default: return p.parseBasicType() case "struct": return p.parseStructType() case "func": // FuncType p.next() return p.parseSignature(nil) case "interface": return p.parseInterfaceType() case "map": return p.parseMapType() case "chan": return p.parseChanType() } case '@': // TypeName pkg, name := p.parseExportedName() return declTypeName(pkg, name).Type() case '[': p.next() // look ahead if p.tok == ']' { // SliceType p.next() return types.NewSlice(p.parseType()) } return p.parseArrayType() case '*': // PointerType p.next() return types.NewPointer(p.parseType()) case '<': return p.parseChanType() case '(': // "(" Type ")" p.next() typ := p.parseType() p.expect(')') return typ } p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) return nil }
// makeLiteralSlice allocates a new slice, storing in it the provided elements. func (c *compiler) makeLiteralSlice(v []llvm.Value, elttyp types.Type) llvm.Value { n := llvm.ConstInt(c.types.inttype, uint64(len(v)), false) eltType := c.types.ToLLVM(elttyp) arrayType := llvm.ArrayType(eltType, len(v)) mem := c.createMalloc(llvm.SizeOf(arrayType)) mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "") for i, value := range v { indices := []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(i), false)} ep := c.builder.CreateGEP(mem, indices, "") c.builder.CreateStore(value, ep) } slicetyp := types.NewSlice(elttyp) struct_ := llvm.Undef(c.types.ToLLVM(slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, n, 1, "") struct_ = c.builder.CreateInsertValue(struct_, n, 2, "") return struct_ }
func (c *rVSliceConstraint) solve(a *analysis, _ *node, delta nodeset) { changed := false for vObj := range delta { tDyn, payload, indirect := a.taggedValue(vObj) if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } var res nodeid switch t := tDyn.Underlying().(type) { case *types.Pointer: if tArr, ok := t.Elem().Underlying().(*types.Array); ok { // pointer to array res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil) if a.onlineCopy(res+1, payload) { a.addWork(res + 1) } } case *types.Array: // TODO(adonovan): implement addressable // arrays when we do indirect tagged objects. case *types.Slice: res = vObj case *types.Basic: if t == types.Typ[types.String] { res = vObj } } if res != 0 && a.addLabel(c.result, res) { changed = true } } if changed { a.addWork(c.result) } }
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(false) if name == "" { name = "_" // cannot access unnamed identifiers } if p.tok == '.' { p.expectSpecial("...") isVariadic = true } typ := p.parseType() if isVariadic { typ = types.NewSlice(typ) } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() } // TODO(gri) should we provide a package? par = types.NewVar(token.NoPos, nil, name, typ) return }
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(false) // remove gc-specific parameter numbering if i := strings.Index(name, "·"); i >= 0 { name = name[:i] } if p.tok == '.' { p.expectSpecial("...") isVariadic = true } typ := p.parseType() if isVariadic { typ = types.NewSlice(typ) } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() } // TODO(gri) should we provide a package? par = types.NewVar(token.NoPos, nil, name, typ) return }
// buildFunction takes a function Value, a list of parameters, and a body, // and generates code for the function. func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.Tuple, body *ast.BlockStmt, isvariadic bool) { if currblock := c.builder.GetInsertBlock(); !currblock.IsNil() { defer c.builder.SetInsertPointAtEnd(currblock) } llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0}) entry := llvm.AddBasicBlock(llvm_fn, "entry") c.builder.SetInsertPointAtEnd(entry) // For closures, context is the captured context values. var paramoffset int if context != nil { paramoffset++ // Store the existing values. We're going to temporarily // replace the values with offsets into the context param. oldvalues := make([]*LLVMValue, context.Len()) for i := range oldvalues { v := context.At(i) oldvalues[i] = c.objectdata[v].Value } defer func() { for i := range oldvalues { v := context.At(i) c.objectdata[v].Value = oldvalues[i] } }() // The context parameter is a pointer to a struct // whose elements are pointers to captured values. arg0 := llvm_fn.Param(0) for i := range oldvalues { v := context.At(i) argptr := c.builder.CreateStructGEP(arg0, i, "") argptr = c.builder.CreateLoad(argptr, "") ptrtyp := oldvalues[i].pointer.Type() newvalue := c.NewValue(argptr, ptrtyp) c.objectdata[v].Value = newvalue.makePointee() } } // Bind receiver, arguments and return values to their // identifiers/objects. We'll store each parameter on the stack so // they're addressable. nparams := int(params.Len()) for i := 0; i < nparams; i++ { v := params.At(i) name := v.Name() if name != "" { value := llvm_fn.Param(i + paramoffset) typ := v.Type() if isvariadic && i == nparams-1 { typ = types.NewSlice(typ) } stackvalue := c.builder.CreateAlloca(c.types.ToLLVM(typ), name) c.builder.CreateStore(value, stackvalue) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar := ptrvalue.makePointee() stackvar.stack = f c.objectdata[v].Value = stackvar } } funcstate := &function{LLVMValue: f, results: results} c.functions.push(funcstate) hasdefer := hasDefer(funcstate, body) // Allocate space on the stack for named results. results.ForEach(func(v *types.Var) { name := v.Name() allocstack := name != "" if !allocstack && hasdefer { c.objectdata[v] = &ObjectData{} allocstack = true } if allocstack { typ := v.Type() llvmtyp := c.types.ToLLVM(typ) stackptr := c.builder.CreateAlloca(llvmtyp, name) c.builder.CreateStore(llvm.ConstNull(llvmtyp), stackptr) ptrvalue := c.NewValue(stackptr, types.NewPointer(typ)) stackvar := ptrvalue.makePointee() stackvar.stack = f c.objectdata[v].Value = stackvar } }) // Create the function body. if hasdefer { c.makeDeferBlock(funcstate, body) } c.VisitBlockStmt(body, false) c.functions.pop() // If the last instruction in the function is not a terminator, then // we either have unreachable code or a missing optional return statement // (the latter case is allowable only for functions without results). // // Use GetInsertBlock rather than LastBasicBlock, since the // last basic block might actually be a "defer" block. last := c.builder.GetInsertBlock() if in := last.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() { c.builder.SetInsertPointAtEnd(last) if results.Len() == 0 { if funcstate.deferblock.IsNil() { c.builder.CreateRetVoid() } else { c.builder.CreateBr(funcstate.deferblock) } } else { c.builder.CreateUnreachable() } } }
func (v *LLVMValue) Convert(dsttyp types.Type) Value { b := v.compiler.builder // If it's a stack allocated value, we'll want to compare the // value type, not the pointer type. srctyp := v.typ // Get the underlying type, if any. origdsttyp := dsttyp dsttyp = dsttyp.Underlying() srctyp = srctyp.Underlying() // Identical (underlying) types? Just swap in the destination type. if types.IsIdentical(srctyp, dsttyp) { // A method converted to a function type without the // receiver is where we convert a "method value" into a // function. if srctyp, ok := srctyp.(*types.Signature); ok && srctyp.Recv() != nil { if dsttyp, ok := dsttyp.(*types.Signature); ok && dsttyp.Recv() == nil { return v.convertMethodValue(origdsttyp) } } // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } // Both pointer types with identical underlying types? Same as above. if srctyp, ok := srctyp.(*types.Pointer); ok { if dsttyp, ok := dsttyp.(*types.Pointer); ok { srctyp := srctyp.Elem().Underlying() dsttyp := dsttyp.Elem().Underlying() if types.IsIdentical(srctyp, dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } } // Convert from an interface type. if _, isinterface := srctyp.(*types.Interface); isinterface { if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.mustConvertI2I(interface_) } else { return v.mustConvertI2V(origdsttyp) } } // Converting to an interface type. if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.convertV2I(interface_) } byteslice := types.NewSlice(types.Typ[types.Byte]) runeslice := types.NewSlice(types.Typ[types.Rune]) // string -> if isString(srctyp) { // (untyped) string -> string // XXX should untyped strings be able to escape go/types? if isString(dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } // string -> []byte if types.IsIdentical(dsttyp, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(strdata.Type().ElementType(), strlen, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(strdata, c.target.IntPtrType(), ""), strlen, }, "") strdata = newdata struct_ := llvm.Undef(c.types.ToLLVM(byteslice)) struct_ = c.builder.CreateInsertValue(struct_, strdata, 0, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 1, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 2, "") return c.NewValue(struct_, byteslice) } // string -> []rune if types.IsIdentical(dsttyp, runeslice) { return v.stringToRuneSlice() } } // []byte -> string if types.IsIdentical(srctyp, byteslice) && isString(dsttyp) { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(data.Type().ElementType(), len, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(data, c.target.IntPtrType(), ""), len, }, "") data = newdata struct_ := llvm.Undef(c.types.ToLLVM(types.Typ[types.String])) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewValue(struct_, types.Typ[types.String]) } // []rune -> string if types.IsIdentical(srctyp, runeslice) && isString(dsttyp) { return v.runeSliceToString() } // rune -> string if isString(dsttyp) && isInteger(srctyp) { return v.runeToString() } // TODO other special conversions? llvm_type := v.compiler.types.ToLLVM(dsttyp) // Unsafe pointer conversions. if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer if _, isptr := srctyp.(*types.Pointer); isptr { value := b.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if srctyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X if _, isptr := dsttyp.(*types.Pointer); isptr { value := b.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if dsttyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } lv := v.LLVMValue() srcType := lv.Type() switch srcType.TypeKind() { case llvm.IntegerTypeKind: switch llvm_type.TypeKind() { case llvm.IntegerTypeKind: srcBits := srcType.IntTypeWidth() dstBits := llvm_type.IntTypeWidth() delta := srcBits - dstBits switch { case delta < 0: // TODO check if (un)signed, use S/ZExt accordingly. lv = b.CreateZExt(lv, llvm_type, "") case delta > 0: lv = b.CreateTrunc(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) case llvm.FloatTypeKind, llvm.DoubleTypeKind: if !isUnsigned(v.Type()) { lv = b.CreateSIToFP(lv, llvm_type, "") } else { lv = b.CreateUIToFP(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = b.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = b.CreateFPExt(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } } // Complex -> complex. Complexes are only convertible to other // complexes, contant conversions aside. So we can just check the // source type here; given that the types are not identical // (checked above), we can assume the destination type is the alternate // complex type. if isComplex(srctyp) { var fpcast func(*Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if srctyp == types.Typ[types.Complex64] { fpcast = (*Builder).CreateFPExt fptype = llvm.DoubleType() } else { fpcast = (*Builder).CreateFPTrunc fptype = llvm.FloatType() } if fpcast != nil { realv := b.CreateExtractValue(lv, 0, "") imagv := b.CreateExtractValue(lv, 1, "") realv = fpcast(b, realv, fptype, "") imagv = fpcast(b, imagv, fptype, "") lv = llvm.Undef(v.compiler.types.ToLLVM(dsttyp)) lv = b.CreateInsertValue(lv, realv, 0, "") lv = b.CreateInsertValue(lv, imagv, 1, "") return v.compiler.NewValue(lv, origdsttyp) } } srcstr := v.compiler.types.TypeString(v.typ) dststr := v.compiler.types.TypeString(origdsttyp) panic(fmt.Sprintf("unimplemented conversion: %s -> %s", srcstr, dststr)) }
// BuiltinCallSignature returns a new Signature describing the // effective type of a builtin operator for the particular call e. // // This requires ad-hoc typing rules for all variadic (append, print, // println) and polymorphic (append, copy, delete, close) built-ins. // This logic could be part of the typechecker, and should arguably // be moved there and made accessible via an additional types.Context // callback. // func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature { var params []*types.Var var isVariadic bool switch builtin := unparen(e.Fun).(*ast.Ident).Name; builtin { case "append": var t0, t1 types.Type t0 = info.TypeOf(e) // infer arg[0] type from result type if e.Ellipsis != 0 { // append(tslice, tslice...) []T // append(byteslice, "foo"...) []byte t1 = info.TypeOf(e.Args[1]) // no conversion } else { // append([]T, x, y, z) []T t1 = t0.Underlying() isVariadic = true } params = append(params, types.NewVar(token.NoPos, nil, "", t0), types.NewVar(token.NoPos, nil, "", t1)) case "print", "println": // print{,ln}(any, ...interface{}) isVariadic = true // Note, arg0 may have any type, not necessarily tEface. params = append(params, types.NewVar(token.NoPos, nil, "", info.TypeOf(e.Args[0])), types.NewVar(token.NoPos, nil, "", types.NewSlice(tEface))) case "close": params = append(params, types.NewVar(token.NoPos, nil, "", info.TypeOf(e.Args[0]))) case "copy": // copy([]T, []T) int // Infer arg types from each other. Sleazy. var st *types.Slice if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok { st = t } else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok { st = t } else { panic("cannot infer types in call to copy()") } stvar := types.NewVar(token.NoPos, nil, "", st) params = append(params, stvar, stvar) case "delete": // delete(map[K]V, K) tmap := info.TypeOf(e.Args[0]) tkey := tmap.Underlying().(*types.Map).Key() params = append(params, types.NewVar(token.NoPos, nil, "", tmap), types.NewVar(token.NoPos, nil, "", tkey)) case "len", "cap": params = append(params, types.NewVar(token.NoPos, nil, "", info.TypeOf(e.Args[0]))) case "real", "imag": // Reverse conversion to "complex" case below. var argType types.Type switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedFloat: argType = types.Typ[types.UntypedComplex] case types.Float64: argType = tComplex128 case types.Float32: argType = tComplex64 default: unreachable() } params = append(params, types.NewVar(token.NoPos, nil, "", argType)) case "complex": var argType types.Type switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedComplex: argType = types.Typ[types.UntypedFloat] case types.Complex128: argType = tFloat64 case types.Complex64: argType = tFloat32 default: unreachable() } v := types.NewVar(token.NoPos, nil, "", argType) params = append(params, v, v) case "panic": params = append(params, types.NewVar(token.NoPos, nil, "", tEface)) case "recover": // no params default: panic("unknown builtin: " + builtin) } return types.NewSignature(nil, nil, types.NewTuple(params...), nil, isVariadic) }
func (c *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) *expression { exprType := c.p.info.Types[expr].Type if types.Identical(exprType, desiredType) { return c.translateExpr(expr) } if c.p.pkg.Path() == "reflect" { if call, isCall := expr.(*ast.CallExpr); isCall && types.Identical(c.p.info.Types[call.Fun].Type, types.Typ[types.UnsafePointer]) { if ptr, isPtr := desiredType.(*types.Pointer); isPtr { if named, isNamed := ptr.Elem().(*types.Named); isNamed { switch named.Obj().Name() { case "arrayType", "chanType", "funcType", "interfaceType", "mapType", "ptrType", "sliceType", "structType": return c.formatExpr("%e.%s", call.Args[0], named.Obj().Name()) // unsafe conversion default: return c.translateExpr(expr) } } } } } switch t := desiredType.Underlying().(type) { case *types.Basic: switch { case t.Info()&types.IsInteger != 0: basicExprType := exprType.Underlying().(*types.Basic) switch { case is64Bit(t): if !is64Bit(basicExprType) { if basicExprType.Kind() == types.Uintptr { // this might be an Object returned from reflect.Value.Pointer() return c.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", c.typeName(desiredType), expr) } return c.formatExpr("new %s(0, %e)", c.typeName(desiredType), expr) } return c.formatExpr("new %1s(%2h, %2l)", c.typeName(desiredType), expr) case is64Bit(basicExprType): if t.Info()&types.IsUnsigned == 0 && basicExprType.Info()&types.IsUnsigned == 0 { return c.fixNumber(c.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t) } return c.fixNumber(c.formatExpr("%s.$low", c.translateExpr(expr)), t) case basicExprType.Info()&types.IsFloat != 0: return c.formatParenExpr("%e >> 0", expr) case types.Identical(exprType, types.Typ[types.UnsafePointer]): return c.translateExpr(expr) default: return c.fixNumber(c.translateExpr(expr), t) } case t.Info()&types.IsFloat != 0: if t.Kind() == types.Float64 && exprType.Underlying().(*types.Basic).Kind() == types.Float32 { return c.formatExpr("$coerceFloat32(%f)", expr) } return c.formatExpr("%f", expr) case t.Info()&types.IsComplex != 0: return c.formatExpr("new %1s(%2r, %2i)", c.typeName(desiredType), expr) case t.Info()&types.IsString != 0: value := c.translateExpr(expr) switch et := exprType.Underlying().(type) { case *types.Basic: if is64Bit(et) { value = c.formatExpr("%s.$low", value) } if et.Info()&types.IsNumeric != 0 { return c.formatExpr("$encodeRune(%s)", value) } return value case *types.Slice: if types.Identical(et.Elem().Underlying(), types.Typ[types.Rune]) { return c.formatExpr("$runesToString(%s)", value) } return c.formatExpr("$bytesToString(%s)", value) default: panic(fmt.Sprintf("Unhandled conversion: %v\n", et)) } case t.Kind() == types.UnsafePointer: if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND { if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr { return c.formatExpr("$sliceToArray(%s)", c.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8]))) } if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" { return c.formatExpr("new Uint8Array(0)") } } if ptr, isPtr := c.p.info.Types[expr].Type.(*types.Pointer); c.p.pkg.Path() == "syscall" && isPtr { if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) c.Delayed(func() { c.Printf("%s = %s, %s;", target, c.translateExpr(expr), c.loadStruct(array, target, s)) }) return c.formatExpr("%s", array) } } if call, ok := expr.(*ast.CallExpr); ok { if id, ok := call.Fun.(*ast.Ident); ok && id.Name == "new" { return c.formatExpr("new Uint8Array(%d)", int(sizes32.Sizeof(c.p.info.Types[call.Args[0]].Type))) } } } case *types.Slice: switch et := exprType.Underlying().(type) { case *types.Basic: if et.Info()&types.IsString != 0 { if types.Identical(t.Elem().Underlying(), types.Typ[types.Rune]) { return c.formatExpr("new %s($stringToRunes(%e))", c.typeName(desiredType), expr) } return c.formatExpr("new %s($stringToBytes(%e))", c.typeName(desiredType), expr) } case *types.Array, *types.Pointer: return c.formatExpr("new %s(%e)", c.typeName(desiredType), expr) } case *types.Pointer: if s, isStruct := t.Elem().Underlying().(*types.Struct); isStruct { if c.p.pkg.Path() == "syscall" && types.Identical(exprType, types.Typ[types.UnsafePointer]) { array := c.newVariable("_array") target := c.newVariable("_struct") return c.formatExpr("(%s = %e, %s = %s, %s, %s)", array, expr, target, c.zeroValue(t.Elem()), c.loadStruct(array, target, s), target) } return c.formatExpr("$clone(%e, %s)", expr, c.typeName(t.Elem())) } if !types.Identical(exprType, types.Typ[types.UnsafePointer]) { return c.formatExpr("new %1s(%2e.$get, %2e.$set)", c.typeName(desiredType), expr) } case *types.Interface: if types.Identical(exprType, types.Typ[types.UnsafePointer]) { return c.translateExpr(expr) } } return c.translateImplicitConversion(expr, desiredType) }
func (c *PkgContext) translateExprToType(expr ast.Expr, desiredType types.Type) string { if desiredType == nil { return c.translateExpr(expr) } if expr == nil { return c.zeroValue(desiredType) } exprType := c.info.Types[expr] // TODO should be fixed in go/types if _, isSlice := exprType.(*types.Slice); isSlice { constValue := c.info.Values[expr] if constValue != nil && constValue.Kind() == exact.String { exprType = types.Typ[types.String] c.info.Types[expr] = exprType } } basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic) if isBasicExpr && basicExprType.Kind() == types.UntypedNil { return c.zeroValue(desiredType) } switch t := desiredType.Underlying().(type) { case *types.Basic: switch { case t.Info()&types.IsInteger != 0: switch { case is64Bit(t): switch { case !is64Bit(basicExprType): return fmt.Sprintf("new %s(0, %s)", c.typeName(desiredType), c.translateExpr(expr)) case !types.IsIdentical(exprType, desiredType): return fmt.Sprintf("(Go$obj = %s, new %s(Go$obj.high, Go$obj.low))", c.translateExpr(expr), c.typeName(desiredType)) } case is64Bit(basicExprType): return fmt.Sprintf("(Go$obj = %s, Go$obj.low + ((Go$obj.high >> 31) * 4294967296))", c.translateExpr(expr)) case basicExprType.Info()&types.IsFloat != 0: return fmt.Sprintf("(%s >> 0)", c.translateExpr(expr)) default: return c.translateExpr(expr) } case t.Info()&types.IsFloat != 0: return c.flatten64(expr) case t.Info()&types.IsString != 0: value := c.translateExpr(expr) switch et := exprType.Underlying().(type) { case *types.Basic: if is64Bit(et) { value = fmt.Sprintf("%s.low", value) } if et.Info()&types.IsNumeric != 0 { return fmt.Sprintf("Go$encodeRune(%s)", value) } return value case *types.Slice: if types.IsIdentical(et.Elem().Underlying(), types.Typ[types.Rune]) { return fmt.Sprintf("Go$runesToString(%s)", value) } return fmt.Sprintf("Go$bytesToString(%s)", value) default: panic(fmt.Sprintf("Unhandled conversion: %v\n", et)) } case t.Kind() == types.UnsafePointer: if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND { if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr { return fmt.Sprintf("Go$sliceToArray(%s)", c.translateExprToType(indexExpr.X, types.NewSlice(nil))) } if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" { return "new Uint8Array(0)" } } if ptr, isPtr := c.info.Types[expr].(*types.Pointer); isPtr { if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) c.Delayed(func() { c.Printf("%s = %s;", target, c.translateExpr(expr)) c.loadStruct(array, target, s) }) return array } } } case *types.Slice: switch et := exprType.Underlying().(type) { case *types.Basic: if et.Info()&types.IsString != 0 { if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Rune]) { return fmt.Sprintf("new %s(Go$stringToRunes(%s))", c.typeName(desiredType), c.translateExpr(expr)) } return fmt.Sprintf("new %s(Go$stringToBytes(%s))", c.typeName(desiredType), c.translateExpr(expr)) } case *types.Array, *types.Pointer: return fmt.Sprintf("new %s(%s)", c.typeName(desiredType), c.translateExpr(expr)) } _, desiredIsNamed := desiredType.(*types.Named) if desiredIsNamed && !types.IsIdentical(exprType, desiredType) { return fmt.Sprintf("(Go$obj = %s, Go$subslice(new %s(Go$obj.array), Go$obj.offset, Go$obj.offset + Go$obj.length))", c.translateExpr(expr), c.typeName(desiredType)) } return c.translateExpr(expr) case *types.Interface: if isWrapped(exprType) { return fmt.Sprintf("new %s(%s)", c.typeName(exprType), c.translateExpr(expr)) } if _, isStruct := exprType.Underlying().(*types.Struct); isStruct { return fmt.Sprintf("(Go$obj = %s, new Go$obj.constructor.Go$NonPointer(Go$obj))", c.translateExpr(expr)) } case *types.Pointer: n, isNamed := t.Elem().(*types.Named) s, isStruct := t.Elem().Underlying().(*types.Struct) if isStruct && types.IsIdentical(exprType, types.Typ[types.UnsafePointer]) { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = %s;", array, c.translateExpr(expr)) c.Printf("%s = %s;", target, c.zeroValue(t.Elem())) c.loadStruct(array, target, s) return target } if isNamed && !types.IsIdentical(exprType, desiredType) { if isStruct { return c.clone(c.translateExpr(expr), t.Elem()) } return fmt.Sprintf("(Go$obj = %s, new %s.Go$Pointer(Go$obj.Go$get, Go$obj.Go$set))", c.translateExpr(expr), c.typeName(n)) } case *types.Struct, *types.Array: if _, isComposite := expr.(*ast.CompositeLit); !isComposite { return c.clone(c.translateExpr(expr), desiredType) } case *types.Chan, *types.Map, *types.Signature: // no converion default: panic(fmt.Sprintf("Unhandled conversion: %v\n", t)) } return c.translateExpr(expr) }
func (c *PkgContext) translateExpr(expr ast.Expr) string { exprType := c.info.Types[expr] if value, valueFound := c.info.Values[expr]; valueFound { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return strconv.FormatBool(exact.BoolVal(value)) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) return fmt.Sprintf("new %s(%d, %d)", c.typeName(exprType), d>>32, d&(1<<32-1)) } d, _ := exact.Int64Val(value) return strconv.FormatInt(d, 10) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return strconv.FormatFloat(f, 'g', -1, 64) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return fmt.Sprintf("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case basic.Info()&types.IsString != 0: buffer := bytes.NewBuffer(nil) for _, r := range []byte(exact.StringVal(value)) { switch r { case '\b': buffer.WriteString(`\b`) case '\f': buffer.WriteString(`\f`) case '\n': buffer.WriteString(`\n`) case '\r': buffer.WriteString(`\r`) case '\t': buffer.WriteString(`\t`) case '\v': buffer.WriteString(`\v`) case '"': buffer.WriteString(`\"`) case '\\': buffer.WriteString(`\\`) default: if r < 0x20 || r > 0x7E { fmt.Fprintf(buffer, `\x%02X`, r) continue } buffer.WriteByte(r) } } return `"` + buffer.String() + `"` default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.info.Values[kve.Key]) i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateExprToType(element, elementType) i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) != 0 { zero := c.zeroValue(t.Elem()) for len(elements) < int(t.Len()) { elements = append(elements, zero) } return createListComposite(t.Elem(), elements) } return fmt.Sprintf("Go$makeArray(%s, %d, function() { return %s; })", toArrayType(t.Elem()), t.Len(), c.zeroValue(t.Elem())) case *types.Slice: return fmt.Sprintf("new %s(%s)", c.typeName(exprType), createListComposite(t.Elem(), collectIndexedElements(t.Elem()))) case *types.Map: elements := make([]string, len(e.Elts)*2) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) elements[i*2] = c.translateExprToType(kve.Key, t.Key()) elements[i*2+1] = c.translateExprToType(kve.Value, t.Elem()) } return fmt.Sprintf("new %s([%s])", c.typeName(exprType), strings.Join(elements, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateExprToType(element, t.Field(i).Type()) } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateExprToType(kve.Value, t.Field(j).Type()) break } } } } if named, isNamed := exprType.(*types.Named); isNamed { return fmt.Sprintf("new %s(%s)", c.objectName(named.Obj()), strings.Join(elements, ", ")) } structVar := c.newVariable("_struct") c.translateTypeSpec(&ast.TypeSpec{ Name: c.newIdent(structVar, t), Type: e.Type, }) return fmt.Sprintf("new %s(%s)", structVar, strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: return strings.TrimSpace(string(c.CatchOutput(func() { c.newScope(func() { params := c.translateParams(e.Type) closurePrefix := "(" closureSuffix := ")" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } c.Printf("%sfunction(%s) {", closurePrefix, strings.Join(params, ", ")) c.Indent(func() { c.translateFunctionBody(e.Body.List, exprType.(*types.Signature)) }) c.Printf("}%s", closureSuffix) }) }))) case *ast.UnaryExpr: switch e.Op { case token.AND: switch c.info.Types[e.X].Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) default: if _, isComposite := e.X.(*ast.CompositeLit); isComposite { return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.translateExpr(e.X), c.typeName(c.info.Types[e])) } closurePrefix := "" closureSuffix := "" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } vVar := c.newVariable("v") return fmt.Sprintf("%snew %s(function() { return %s; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), c.translateExpr(e.X), vVar, c.translateAssign(e.X, vVar), closureSuffix) } case token.ARROW: return "undefined" } t := c.info.Types[e.X] basic := t.Underlying().(*types.Basic) op := e.Op.String() switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.high, -%s.low))", x, c.translateExpr(e.X), c.typeName(t), x, x) } if basic.Info()&types.IsComplex != 0 { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.real, -%s.imag))", x, c.translateExpr(e.X), c.typeName(t), x, x) } case token.XOR: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(~%s.high, ~%s.low >>> 0))", x, c.translateExpr(e.X), c.typeName(t), x, x) } op = "~" } return fixNumber(fmt.Sprintf("%s%s", op, c.translateExpr(e.X)), basic) case *ast.BinaryExpr: if e.Op == token.NEQ { return fmt.Sprintf("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.info.Types[e.X] t2 := c.info.Types[e.Y] _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { if is64Bit(basic) { var expr string switch e.Op { case token.MUL: return fmt.Sprintf("Go$mul64(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.QUO: return fmt.Sprintf("Go$div64(%s, %s, false)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.REM: return fmt.Sprintf("Go$div64(%s, %s, true)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL: return fmt.Sprintf("Go$shiftLeft64(%s, %s)", c.translateExpr(e.X), c.flatten64(e.Y)) case token.SHR: return fmt.Sprintf("Go$shiftRight%s(%s, %s)", toJavaScriptType(basic), c.translateExpr(e.X), c.flatten64(e.Y)) case token.EQL: expr = "x.high === y.high && x.low === y.low" case token.LSS: expr = "x.high < y.high || (x.high === y.high && x.low < y.low)" case token.LEQ: expr = "x.high < y.high || (x.high === y.high && x.low <= y.low)" case token.GTR: expr = "x.high > y.high || (x.high === y.high && x.low > y.low)" case token.GEQ: expr = "x.high > y.high || (x.high === y.high && x.low >= y.low)" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.high %s y.high, x.low %s y.low)", c.typeName(t), e.Op, e.Op) case token.AND, token.OR, token.XOR: expr = fmt.Sprintf("new %s(x.high %s y.high, (x.low %s y.low) >>> 0)", c.typeName(t), e.Op, e.Op) case token.AND_NOT: expr = fmt.Sprintf("new %s(x.high &~ y.high, (x.low &~ y.low) >>> 0)", c.typeName(t)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } if basic.Info()&types.IsComplex != 0 { var expr string switch e.Op { case token.EQL: expr = "x.real === y.real && x.imag === y.imag" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.real %s y.real, x.imag %s y.imag)", c.typeName(t), e.Op, e.Op) case token.MUL: expr = fmt.Sprintf("new %s(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real)", c.typeName(t)) case token.QUO: return fmt.Sprintf("Go$divComplex(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } switch e.Op { case token.EQL: return fmt.Sprintf("%s === %s", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.LSS, token.LEQ, token.GTR, token.GEQ: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.ADD, token.SUB: return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.MUL: if basic.Kind() == types.Int32 { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >> 0) + (%s << 16 >>> 16) * %s) >> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } if basic.Kind() == types.Uint32 || basic.Kind() == types.Uintptr { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >>> 0) + (%s << 16 >>> 16) * %s) >>> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } return fixNumber(fmt.Sprintf("%s * %s", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) case token.QUO: value := fmt.Sprintf("%s / %s", c.translateExpr(e.X), c.translateExpr(e.Y)) if basic.Info()&types.IsInteger != 0 { value = "(Go$obj = " + value + `, (Go$obj === Go$obj && Go$obj !== 1/0 && Go$obj !== -1/0) ? Go$obj : Go$throwRuntimeError("integer divide by zero"))` } switch basic.Kind() { case types.Int, types.Uint: return "(" + value + " >> 0)" // cut off decimals default: return fixNumber(value, basic) } case token.REM: return fmt.Sprintf(`(Go$obj = %s %% %s, Go$obj === Go$obj ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`, c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.info.Values[e.Y] != nil { return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), op, c.translateExpr(e.Y)), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return fixNumber(fmt.Sprintf("(%s >> Go$min(%s, 31))", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) } y := c.newVariable("y") return fixNumber(fmt.Sprintf("(%s = %s, %s < 32 ? (%s %s %s) : 0)", y, c.translateExprToType(e.Y, types.Typ[types.Uint]), y, c.translateExpr(e.X), op, y), basic) case token.AND, token.OR, token.XOR: return fixNumber(fmt.Sprintf("(%s %s %s)", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.AND_NOT: return fixNumber(fmt.Sprintf("(%s &~ %s)", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LAND, token.LOR: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.EQL: switch u := t.Underlying().(type) { case *types.Struct: x := c.newVariable("x") y := c.newVariable("y") var conds []string for i := 0; i < u.NumFields(); i++ { field := u.Field(i) if field.Name() == "_" { continue } conds = append(conds, c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(x+"."+field.Name(), field.Type()), Op: token.EQL, Y: c.newIdent(y+"."+field.Name(), field.Type()), })) } if len(conds) == 0 { conds = []string{"true"} } return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), strings.Join(conds, " && ")) case *types.Interface: return fmt.Sprintf("Go$interfaceIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return fmt.Sprintf("Go$sliceIsEqual(%s, %s, %s, %s)", c.translateExpr(xIndex.X), c.flatten64(xIndex.Index), c.translateExpr(yIndex.X), c.flatten64(yIndex.Index)) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) default: return fmt.Sprintf("Go$pointerIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) } default: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) } default: panic(e.Op) } case *ast.ParenExpr: return fmt.Sprintf("(%s)", c.translateExpr(e.X)) case *ast.IndexExpr: xType := c.info.Types[e.X] if ptr, isPointer := xType.(*types.Pointer); isPointer { xType = ptr.Elem() } switch t := xType.Underlying().(type) { case *types.Array: return fmt.Sprintf("%s[%s]", c.translateExpr(e.X), c.flatten64(e.Index)) case *types.Slice: sliceVar := c.newVariable("_slice") indexVar := c.newVariable("_index") return fmt.Sprintf(`(%s = %s, %s = %s, (%s >= 0 && %s < %s.length) ? %s.array[%s.offset + %s] : Go$throwRuntimeError("index out of range"))`, sliceVar, c.translateExpr(e.X), indexVar, c.flatten64(e.Index), indexVar, indexVar, sliceVar, sliceVar, sliceVar, indexVar) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? [Go$obj.v, true] : [%s, false])`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) } return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? Go$obj.v : %s)`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) case *types.Basic: return fmt.Sprintf("%s.charCodeAt(%s)", c.translateExpr(e.X), c.flatten64(e.Index)) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: b, isBasic := c.info.Types[e.X].(*types.Basic) isString := isBasic && b.Info()&types.IsString != 0 slice := c.translateExprToType(e.X, exprType) if e.High == nil { if e.Low == nil { return slice } if isString { return fmt.Sprintf("%s.substring(%s)", slice, c.flatten64(e.Low)) } return fmt.Sprintf("Go$subslice(%s, %s)", slice, c.flatten64(e.Low)) } low := "0" if e.Low != nil { low = c.flatten64(e.Low) } if isString { return fmt.Sprintf("%s.substring(%s, %s)", slice, low, c.flatten64(e.High)) } if e.Max != nil { return fmt.Sprintf("Go$subslice(%s, %s, %s, %s)", slice, low, c.flatten64(e.High), c.flatten64(e.Max)) } return fmt.Sprintf("Go$subslice(%s, %s, %s)", slice, low, c.flatten64(e.High)) case *ast.SelectorExpr: sel := c.info.Selections[e] parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: return c.translateExpr(e.X) + "." + translateSelection(sel) case types.MethodVal: parameters := makeParametersList() recv := c.newVariable("_recv") return fmt.Sprintf("(%s = %s, function(%s) { return %s.%s(%s); })", recv, c.translateExpr(e.X), strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return fmt.Sprintf("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) case types.PackageObj: return fmt.Sprintf("%s.%s", c.translateExpr(e.X), e.Sel.Name) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } switch f := plainFun.(type) { case *ast.Ident: switch o := c.info.Objects[f].(type) { case *types.Builtin: switch o.Name() { case "new": t := c.info.Types[e].(*types.Pointer) if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return "new Uint8Array(8)" } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.zeroValue(t.Elem()) default: return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch t2 := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: if len(e.Args) == 3 { return fmt.Sprintf("Go$subslice(new %s(Go$makeArray(%s, %s, function() { return %s; })), 0, %s)", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[2], types.Typ[types.Int]), c.zeroValue(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int])) } return fmt.Sprintf("new %s(Go$makeArray(%s, %s, function() { return %s; }))", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]), c.zeroValue(t2.Elem())) default: args := []string{"undefined"} for _, arg := range e.Args[1:] { args = append(args, c.translateExpr(arg)) } return fmt.Sprintf("new %s(%s)", c.typeName(c.info.Types[e.Args[0]]), strings.Join(args, ", ")) } case "len": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Basic, *types.Slice: return arg + ".length" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Map: return fmt.Sprintf("(Go$obj = %s, Go$obj !== null ? Go$keys(Go$obj).length : 0)", arg) case *types.Chan: return "0" // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: return arg + ".capacity" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Chan: return "0" // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return fmt.Sprintf("throw new Go$Panic(%s)", c.translateExprToType(e.Args[0], types.NewInterface(nil, nil))) case "append": if e.Ellipsis.IsValid() { return fmt.Sprintf("Go$append(%s, %s)", c.translateExpr(e.Args[0]), c.translateExprToType(e.Args[1], exprType)) } sliceType := exprType.Underlying().(*types.Slice) toAppend := createListComposite(sliceType.Elem(), c.translateExprSlice(e.Args[1:], sliceType.Elem())) return fmt.Sprintf("Go$append(%s, new %s(%s))", c.translateExpr(e.Args[0]), c.typeName(exprType), toAppend) case "delete": return fmt.Sprintf(`delete (%s || Go$Map.Go$nil)[%s]`, c.translateExpr(e.Args[0]), c.makeKey(e.Args[1], c.info.Types[e.Args[0]].Underlying().(*types.Map).Key())) case "copy": return fmt.Sprintf("Go$copy(%s, %s)", c.translateExprToType(e.Args[0], types.NewSlice(types.Typ[types.Byte])), c.translateExprToType(e.Args[1], types.NewSlice(types.Typ[types.Byte]))) case "print", "println": return fmt.Sprintf("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", ")) case "complex": return fmt.Sprintf("new %s(%s, %s)", c.typeName(c.info.Types[e]), c.translateExpr(e.Args[0]), c.translateExpr(e.Args[1])) case "real": return c.translateExpr(e.Args[0]) + ".real" case "imag": return c.translateExpr(e.Args[0]) + ".imag" case "recover": return "Go$recover()" case "close": return `Go$throwRuntimeError("not supported by GopherJS: close")` default: panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name())) } case *types.TypeName: // conversion if basic, isBasic := o.Type().Underlying().(*types.Basic); isBasic && !types.IsIdentical(c.info.Types[e.Args[0]], types.Typ[types.UnsafePointer]) { return fixNumber(c.translateExprToType(e.Args[0], o.Type()), basic) } return c.translateExprToType(e.Args[0], o.Type()) } case *ast.FuncType: // conversion return c.translateExprToType(e.Args[0], c.info.Types[plainFun]) } funType := c.info.Types[plainFun] sig, isSig := funType.Underlying().(*types.Signature) if !isSig { // conversion if c.pkg.Path() == "reflect" { if call, isCall := e.Args[0].(*ast.CallExpr); isCall && types.IsIdentical(c.info.Types[call.Fun], types.Typ[types.UnsafePointer]) { if named, isNamed := funType.(*types.Pointer).Elem().(*types.Named); isNamed { return c.translateExpr(call.Args[0]) + "." + named.Obj().Name() // unsafe conversion } } } return c.translateExprToType(e.Args[0], funType) } var fun string switch f := plainFun.(type) { case *ast.SelectorExpr: sel := c.info.Selections[f] switch sel.Kind() { case types.MethodVal: methodsRecvType := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) _, isPointer := sel.Recv().Underlying().(*types.Pointer) _, isStruct := sel.Recv().Underlying().(*types.Struct) _, isArray := sel.Recv().Underlying().(*types.Array) if pointerExpected && !isPointer && !isStruct && !isArray { target := c.translateExpr(f.X) vVar := c.newVariable("v") fun = fmt.Sprintf("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), target, vVar, target, vVar, f.Sel.Name) break } if isWrapped(sel.Recv()) { fun = fmt.Sprintf("(new %s(%s)).%s", c.typeName(sel.Recv()), c.translateExpr(f.X), f.Sel.Name) break } fun = fmt.Sprintf("%s.%s", c.translateExpr(f.X), f.Sel.Name) case types.FieldVal, types.MethodExpr, types.PackageObj: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } if len(e.Args) == 1 { if tuple, isTuple := c.info.Types[e.Args[0]].(*types.Tuple); isTuple { args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type()) } return fmt.Sprintf("(Go$tuple = %s, %s(%s))", c.translateExpr(e.Args[0]), fun, c.translateArgs(sig, args, false)) } } return fmt.Sprintf("%s(%s)", fun, c.translateArgs(sig, e.Args, e.Ellipsis.IsValid())) case *ast.StarExpr: if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.IsIdentical(c.info.Types[c2.Fun], types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.translateExpr(e.X) + ".Go$get()" case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.info.Types[e.Type] check := "Go$obj !== null && " + c.typeCheck("Go$obj.constructor", t) value := "Go$obj" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { value += ".Go$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf("(Go$obj = %s, %s ? [%s, true] : [%s, false])", c.translateExpr(e.X), check, value, c.zeroValue(c.info.Types[e.Type])) } return fmt.Sprintf("(Go$obj = %s, %s ? %s : Go$typeAssertionFailed(Go$obj))", c.translateExpr(e.X), check, value) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := c.info.Objects[e].(type) { case *types.PkgName: return c.pkgVars[o.Pkg().Path()] case *types.Var, *types.Const: return c.objectName(o) case *types.Func: return c.objectName(o) case *types.TypeName: return c.typeName(o.Type()) case *types.Nil: return c.zeroValue(c.info.Types[e]) case nil: return e.Name default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case nil: return "" default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (c *compiler) NewConstValue(v exact.Value, typ types.Type) *LLVMValue { switch { case v.Kind() == exact.Nil: llvmtyp := c.types.ToLLVM(typ) return c.NewValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := c.types.ToLLVM(typ) strval := exact.StringVal(v) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { init := llvm.ConstString(strval, false) ptr = llvm.AddGlobal(c.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(c.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return c.NewValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := c.types.ToLLVM(typ) var llvmvalue llvm.Value if isUnsigned(typ) { v, _ := exact.Uint64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, v, false) } else { v, _ := exact.Int64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true) } return c.NewValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } var llvmvalue llvm.Value if exact.BoolVal(v) { llvmvalue = llvm.ConstAllOnes(llvm.Int1Type()) } else { llvmvalue = llvm.ConstNull(llvm.Int1Type()) } return c.NewValue(llvmvalue, typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := c.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return c.NewValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := c.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(llvmtyp, v, false) return c.NewValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := c.types.ToLLVM(typ) floattyp := llvmtyp.StructElementTypes()[0] llvmvalue := llvm.ConstNull(llvmtyp) realv := exact.Real(v) imagv := exact.Imag(v) realfloatval, _ := exact.Float64Val(realv) imagfloatval, _ := exact.Float64Val(imagv) llvmre := llvm.ConstFloat(floattyp, realfloatval) llvmim := llvm.ConstFloat(floattyp, imagfloatval) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1}) return c.NewValue(llvmvalue, typ) } // Special case for string -> []byte if types.IsIdentical(typ.Underlying(), types.NewSlice(types.Typ[types.Byte])) { if v.Kind() == exact.String { strval := c.NewConstValue(v, types.Typ[types.String]) return strval.Convert(typ).(*LLVMValue) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", c.types.TypeString(typ), typ, v, v)) }
func (c *compiler) VisitSliceExpr(expr *ast.SliceExpr) Value { value := c.VisitExpr(expr.X) var low llvm.Value if expr.Low != nil { lowvalue := c.VisitExpr(expr.Low).Convert(types.Typ[types.Int]) low = lowvalue.LLVMValue() } else { low = llvm.ConstNull(c.types.inttype) } var high llvm.Value if expr.High != nil { highvalue := c.VisitExpr(expr.High).Convert(types.Typ[types.Int]) high = highvalue.LLVMValue() } else { high = llvm.ConstAllOnes(c.types.inttype) // -1 } if _, ok := value.Type().Underlying().(*types.Pointer); ok { value = value.(*LLVMValue).makePointee() } switch typ := value.Type().Underlying().(type) { case *types.Array: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int) slice") i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := llvm.Undef(i8slice) // temporary slice arrayptr := value.(*LLVMValue).pointer.LLVMValue() arrayptr = c.builder.CreateBitCast(arrayptr, i8slice.StructElementTypes()[0], "") arraylen := llvm.ConstInt(c.llvmtypes.inttype, uint64(typ.Len()), false) sliceValue = c.builder.CreateInsertValue(sliceValue, arrayptr, 0, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 1, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 2, "") sliceTyp := types.NewSlice(typ.Elem()) runtimeTyp := c.types.ToRuntime(sliceTyp) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low, high} result := c.builder.CreateCall(sliceslice, args, "") llvmSliceTyp := c.types.ToLLVM(sliceTyp) return c.NewValue(c.coerceSlice(result, llvmSliceTyp), sliceTyp) case *types.Slice: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int) slice") i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := value.LLVMValue() sliceTyp := sliceValue.Type() sliceValue = c.coerceSlice(sliceValue, i8slice) runtimeTyp := c.types.ToRuntime(value.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low, high} result := c.builder.CreateCall(sliceslice, args, "") return c.NewValue(c.coerceSlice(result, sliceTyp), value.Type()) case *types.Basic: stringslice := c.NamedFunction("runtime.stringslice", "func f(a string, low, high int) string") args := []llvm.Value{value.LLVMValue(), low, high} result := c.builder.CreateCall(stringslice, args, "") return c.NewValue(result, value.Type()) default: panic("unimplemented") } panic("unreachable") }