func (v ConstValue) Convert(dst_typ types.Type) Value { // Get the underlying type, if any. if name, isname := dst_typ.(*types.Name); isname { dst_typ = types.Underlying(name) } if !types.Identical(v.typ, dst_typ) { // Get the Basic type. isBasic := false if name, isname := types.Underlying(dst_typ).(*types.Name); isname { _, isBasic = name.Underlying.(*types.Basic) } compiler := v.compiler if isBasic { return ConstValue{*v.Const.Convert(&dst_typ), compiler, dst_typ} } else { return compiler.NewLLVMValue(v.LLVMValue(), v.Type()).Convert(dst_typ) //panic(fmt.Errorf("unhandled conversion from %v to %v", v.typ, dst_typ)) } } else { // TODO convert to dst type. ConstValue may need to change to allow // storage of types other than Basic. } return v }
func (v *LLVMValue) UnaryOp(op token.Token) Value { b := v.compiler.builder switch op { case token.SUB: var value llvm.Value isfp := types.Identical(types.Underlying(v.typ), types.Float32) || types.Identical(types.Underlying(v.typ), types.Float64) if isfp { zero := llvm.ConstNull(v.compiler.types.ToLLVM(v.Type())) value = b.CreateFSub(zero, v.LLVMValue(), "") } else { value = b.CreateNeg(v.LLVMValue(), "") } return v.compiler.NewLLVMValue(value, v.typ) case token.ADD: return v // No-op case token.AND: return v.pointer case token.NOT: value := b.CreateNot(v.LLVMValue(), "") return v.compiler.NewLLVMValue(value, v.typ) case token.XOR: lhs := v.LLVMValue() rhs := llvm.ConstAllOnes(lhs.Type()) value := b.CreateXor(lhs, rhs, "") return v.compiler.NewLLVMValue(value, v.typ) default: panic("Unhandled operator: ") // + expr.Op) } panic("unreachable") }
func (c *compiler) VisitSliceExpr(expr *ast.SliceExpr) Value { // expr.X, expr.Low, expr.High value := c.VisitExpr(expr.X) var low, high llvm.Value if expr.Low != nil { low = c.VisitExpr(expr.Low).Convert(types.Int32).LLVMValue() } else { low = llvm.ConstNull(llvm.Int32Type()) } if expr.High != nil { high = c.VisitExpr(expr.High).Convert(types.Int32).LLVMValue() } else { high = llvm.ConstAllOnes(llvm.Int32Type()) // -1 } if _, ok := types.Underlying(value.Type()).(*types.Pointer); ok { value = value.(*LLVMValue).makePointee() } switch typ := types.Underlying(value.Type()).(type) { case *types.Array: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int32) 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(llvm.Int32Type(), 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.Slice{Elt: typ.Elt} 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.NewLLVMValue(c.coerceSlice(result, llvmSliceTyp), sliceTyp) case *types.Slice: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int32) 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.NewLLVMValue(c.coerceSlice(result, sliceTyp), value.Type()) case *types.Name: // String stringslice := c.NamedFunction("runtime.stringslice", "func f(a string, low, high int32) string") args := []llvm.Value{value.LLVMValue(), low, high} result := c.builder.CreateCall(stringslice, args, "") return c.NewLLVMValue(result, value.Type()) default: panic("unimplemented") } panic("unreachable") }
func (c *compiler) VisitLen(expr *ast.CallExpr) Value { if len(expr.Args) > 1 { panic("Expecting only one argument to len") } value := c.VisitExpr(expr.Args[0]) typ := value.Type() if name, ok := types.Underlying(typ).(*types.Name); ok { typ = name.Underlying } switch typ := types.Underlying(typ).(type) { case *types.Pointer: // XXX Converting to a string to be converted back to an int is // silly. The values need an overhaul? Perhaps have types based // on fundamental types, with the additional methods to make // them llgo.Value's. if a, isarray := typ.Base.(*types.Array); isarray { return c.NewConstValue(token.INT, strconv.FormatUint(a.Len, 10)) } v := strconv.FormatUint(uint64(unsafe.Sizeof(uintptr(0))), 10) return c.NewConstValue(token.INT, v) case *types.Slice: sliceval := value.LLVMValue() lenval := c.builder.CreateExtractValue(sliceval, 1, "") return c.NewLLVMValue(lenval, types.Int32).Convert(types.Int) case *types.Map: mapval := value.LLVMValue() f := c.NamedFunction("runtime.maplen", "func f(m uintptr) int") lenval := c.builder.CreateCall(f, []llvm.Value{mapval}, "") return c.NewLLVMValue(lenval, types.Int) case *types.Array: v := strconv.FormatUint(typ.Len, 10) return c.NewConstValue(token.INT, v) case *types.Basic: if typ == types.String.Underlying { switch value := value.(type) { case *LLVMValue: ptr := value.pointer len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "") len_value := c.builder.CreateLoad(len_field, "") return c.NewLLVMValue(len_value, types.Int32).Convert(types.Int) case ConstValue: s := value.Val.(string) n := uint64(len(s)) return c.NewConstValue(token.INT, strconv.FormatUint(n, 10)) } } } panic(fmt.Sprint("Unhandled value type: ", value.Type())) }
func (c *compiler) VisitIndexExpr(expr *ast.IndexExpr) Value { value := c.VisitExpr(expr.X) index := c.VisitExpr(expr.Index) typ := types.Underlying(value.Type()) if typ == types.String { ptr := c.builder.CreateExtractValue(value.LLVMValue(), 0, "") gepindices := []llvm.Value{index.LLVMValue()} ptr = c.builder.CreateGEP(ptr, gepindices, "") result := c.NewLLVMValue(ptr, &types.Pointer{Base: types.Byte}) return result.makePointee() } // We can index a pointer to an array. if _, ok := typ.(*types.Pointer); ok { value = value.(*LLVMValue).makePointee() typ = value.Type() } switch typ := types.Underlying(typ).(type) { case *types.Array: index := index.LLVMValue() var ptr llvm.Value value := value.(*LLVMValue) if value.pointer != nil { ptr = value.pointer.LLVMValue() } else { init := value.LLVMValue() ptr = c.builder.CreateAlloca(init.Type(), "") c.builder.CreateStore(init, ptr) } zero := llvm.ConstNull(llvm.Int32Type()) element := c.builder.CreateGEP(ptr, []llvm.Value{zero, index}, "") result := c.NewLLVMValue(element, &types.Pointer{Base: typ.Elt}) return result.makePointee() case *types.Slice: index := index.LLVMValue() ptr := c.builder.CreateExtractValue(value.LLVMValue(), 0, "") element := c.builder.CreateGEP(ptr, []llvm.Value{index}, "") result := c.NewLLVMValue(element, &types.Pointer{Base: typ.Elt}) return result.makePointee() case *types.Map: value, _ = c.mapLookup(value.(*LLVMValue), index, false) return value } panic(fmt.Sprintf("unreachable (%s)", typ)) }
func (c *compiler) VisitMake(expr *ast.CallExpr) Value { typ := c.types.expr[expr] switch utyp := types.Underlying(typ).(type) { case *types.Slice: var length, capacity Value switch len(expr.Args) { case 3: capacity = c.VisitExpr(expr.Args[2]) fallthrough case 2: length = c.VisitExpr(expr.Args[1]) } slice := c.makeSlice(utyp.Elt, length, capacity) return c.NewLLVMValue(slice, typ) case *types.Chan: f := c.NamedFunction("runtime.makechan", "func f(t uintptr, cap uint32) uintptr") dyntyp := c.types.ToRuntime(typ) dyntyp = c.builder.CreatePtrToInt(dyntyp, c.target.IntPtrType(), "") var cap_ llvm.Value if len(expr.Args) > 1 { cap_ = c.VisitExpr(expr.Args[1]).LLVMValue() } args := []llvm.Value{dyntyp, cap_} ptr := c.builder.CreateCall(f, args, "") return c.NewLLVMValue(ptr, typ) case *types.Map: f := c.NamedFunction("runtime.makemap", "func f(t uintptr) uintptr") dyntyp := c.types.ToRuntime(typ) dyntyp = c.builder.CreatePtrToInt(dyntyp, c.target.IntPtrType(), "") mapval := c.builder.CreateCall(f, []llvm.Value{dyntyp}, "") return c.NewLLVMValue(mapval, typ) } panic(fmt.Sprintf("unhandled type: %s", typ)) }
// destructureExpr evaluates the right-hand side of a // multiple assignment where the right-hand side is a single expression. func (c *compiler) destructureExpr(x ast.Expr) []Value { var values []Value switch x := x.(type) { case *ast.IndexExpr: // value, ok := m[k] m := c.VisitExpr(x.X).(*LLVMValue) index := c.VisitExpr(x.Index) value, notnull := c.mapLookup(m, index, false) values = []Value{value, notnull} case *ast.CallExpr: value := c.VisitExpr(x) aggregate := value.LLVMValue() struct_type := value.Type().(*types.Struct) values = make([]Value, len(struct_type.Fields)) for i, f := range struct_type.Fields { t := f.Type.(types.Type) value_ := c.builder.CreateExtractValue(aggregate, i, "") values[i] = c.NewLLVMValue(value_, t) } case *ast.TypeAssertExpr: lhs := c.VisitExpr(x.X).(*LLVMValue) typ := c.types.expr[x] switch typ := types.Underlying(typ).(type) { case *types.Interface: value, ok := lhs.convertI2I(typ) values = []Value{value, ok} default: value, ok := lhs.convertI2V(typ) values = []Value{value, ok} } } return values }
func (c *compiler) VisitIndexExpr(expr *ast.IndexExpr) Value { value := c.VisitExpr(expr.X).(*LLVMValue) index := c.VisitExpr(expr.Index) typ := value.Type() if typ == types.String { ptr := c.builder.CreateExtractValue(value.LLVMValue(), 0, "") gepindices := []llvm.Value{index.LLVMValue()} ptr = c.builder.CreateGEP(ptr, gepindices, "") result := c.NewLLVMValue(ptr, &types.Pointer{Base: types.Byte}) return result.makePointee() } // We can index a pointer to an array. if _, ok := types.Underlying(typ).(*types.Pointer); ok { value = value.makePointee() typ = value.Type() } switch types.Underlying(typ).(type) { case *types.Array, *types.Slice: var gep_indices []llvm.Value var ptr llvm.Value var result_type types.Type switch typ := types.Underlying(typ).(type) { case *types.Array: // FIXME what to do if value is not addressable? // Do we have to load the array onto the stack? result_type = typ.Elt ptr = value.pointer.LLVMValue() gep_indices = append(gep_indices, llvm.ConstNull(llvm.Int32Type())) case *types.Slice: result_type = typ.Elt ptr = c.builder.CreateExtractValue(value.LLVMValue(), 0, "") } gep_indices = append(gep_indices, index.LLVMValue()) element := c.builder.CreateGEP(ptr, gep_indices, "") result := c.NewLLVMValue(element, &types.Pointer{Base: result_type}) return result.makePointee() case *types.Map: value, _ = c.mapLookup(value, index, false) return value } panic(fmt.Sprintf("unreachable (%s)", typ)) }
func (v ConstValue) LLVMValue() llvm.Value { typ := types.Underlying(v.Type()) switch typ { case types.Int, types.Uint: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), true) // TODO 32/64bit (probably wait for gc) //int_val := v.Val.(*big.Int) //if int_val.Cmp(maxBigInt32) > 0 || int_val.Cmp(minBigInt32) < 0 { // panic(fmt.Sprint("const ", int_val, " overflows int")) //} //return llvm.ConstInt(v.compiler.target.IntPtrType(), uint64(v.Int64()), true) case types.Uint: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), false) case types.Int8: return llvm.ConstInt(llvm.Int8Type(), uint64(v.Int64()), true) case types.Uint8, types.Byte: return llvm.ConstInt(llvm.Int8Type(), uint64(v.Int64()), false) case types.Int16: return llvm.ConstInt(llvm.Int16Type(), uint64(v.Int64()), true) case types.Uint16: return llvm.ConstInt(llvm.Int16Type(), uint64(v.Int64()), false) case types.Int32, types.Rune: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), true) case types.Uint32: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), false) case types.Int64: return llvm.ConstInt(llvm.Int64Type(), uint64(v.Int64()), true) case types.Uint64: return llvm.ConstInt(llvm.Int64Type(), uint64(v.Int64()), true) case types.Float32: return llvm.ConstFloat(llvm.FloatType(), float64(v.Float64())) case types.Float64: return llvm.ConstFloat(llvm.DoubleType(), float64(v.Float64())) case types.UnsafePointer, types.Uintptr: inttype := v.compiler.target.IntPtrType() return llvm.ConstInt(inttype, uint64(v.Int64()), false) case types.String: strval := (v.Val).(string) ptr := v.compiler.builder.CreateGlobalStringPtr(strval, "") len_ := llvm.ConstInt(llvm.Int32Type(), uint64(len(strval)), false) return llvm.ConstStruct([]llvm.Value{ptr, len_}, false) case types.Bool: if v := v.Val.(bool); v { return llvm.ConstAllOnes(llvm.Int1Type()) } return llvm.ConstNull(llvm.Int1Type()) } panic(fmt.Errorf("Unhandled type: %v", typ)) //v.typ.Kind)) }
func (v *LLVMValue) chanRecv() *LLVMValue { c := v.compiler elttyp := types.Underlying(v.typ).(*types.Chan).Elt ptr := c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "") uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") f := c.NamedFunction("runtime.chanrecv", "func f(c, ptr uintptr)") c.builder.CreateCall(f, []llvm.Value{v.LLVMValue(), uintptr_}, "") value := c.builder.CreateLoad(ptr, "") return c.NewLLVMValue(value, elttyp) }
// convertI2I converts an interface to another interface. func (v *LLVMValue) convertI2I(iface *types.Interface) (result Value, success Value) { c := v.compiler builder := v.compiler.builder src_typ := types.Underlying(v.Type()) vptr := v.pointer.LLVMValue() zero_iface_struct := llvm.ConstNull(c.types.ToLLVM(iface)) iface_struct := zero_iface_struct dynamicType := builder.CreateLoad(builder.CreateStructGEP(vptr, 0, ""), "") receiver := builder.CreateLoad(builder.CreateStructGEP(vptr, 1, ""), "") // TODO check whether the functions in the struct take // value or pointer receivers. // TODO handle dynamic interface conversion (non-subset). methods := src_typ.(*types.Interface).Methods for i, m := range iface.Methods { // TODO make this loop linear by iterating through the // interface methods and type methods together. mi := sort.Search(len(methods), func(i int) bool { return methods[i].Name >= m.Name }) if mi >= len(methods) || methods[mi].Name != m.Name { //panic("Failed to locate method: " + m.Name) goto check_dynamic } else { method := builder.CreateStructGEP(vptr, mi+2, "") fptr := builder.CreateLoad(method, "") iface_struct = builder.CreateInsertValue(iface_struct, fptr, i+2, "") } } iface_struct = builder.CreateInsertValue(iface_struct, dynamicType, 0, "") iface_struct = builder.CreateInsertValue(iface_struct, receiver, 1, "") result = c.NewLLVMValue(iface_struct, iface) success = ConstValue{types.Const{true}, c, types.Bool} return result, success check_dynamic: runtimeConvertI2I := c.NamedFunction("runtime.convertI2I", "func f(typ, from, to uintptr) bool") llvmUintptr := runtimeConvertI2I.Type().ElementType().ParamTypes()[0] runtimeType := c.builder.CreatePtrToInt(c.types.ToRuntime(iface), llvmUintptr, "") from := c.builder.CreatePtrToInt(vptr, llvmUintptr, "") to := c.builder.CreateAlloca(iface_struct.Type(), "") c.builder.CreateStore(iface_struct, to) toUintptr := c.builder.CreatePtrToInt(to, llvmUintptr, "") args := []llvm.Value{runtimeType, from, toUintptr} ok := c.builder.CreateCall(runtimeConvertI2I, args, "") value := c.builder.CreateLoad(to, "") value = c.builder.CreateSelect(ok, value, zero_iface_struct, "") result = c.NewLLVMValue(value, iface) success = c.NewLLVMValue(ok, types.Bool) return result, success }
func (c *compiler) VisitTypeSpec(spec *ast.TypeSpec) { obj := spec.Name.Obj type_, istype := (obj.Type).(types.Type) if !istype { type_ = c.GetType(spec.Type) if name, isname := type_.(*types.Name); isname { type_ = types.Underlying(name) } obj.Type = &types.Name{Underlying: type_, Obj: obj} } }
func (tm *TypeMap) ToLLVM(t types.Type) llvm.Type { t = types.Underlying(t) lt, ok := tm.types[t] if !ok { lt = tm.makeLLVMType(t) if lt.IsNil() { panic(fmt.Sprint("Failed to create LLVM type for: ", t)) } tm.types[t] = lt } return lt }
func (tm *TypeMap) ToRuntime(t types.Type) llvm.Value { t = types.Underlying(t) r, ok := tm.runtime[t] if !ok { _, r = tm.makeRuntimeType(t) if r.IsNil() { panic(fmt.Sprint("Failed to create runtime type for: ", t)) } tm.runtime[t] = r } return r }
func (v ConstValue) Convert(dstTyp types.Type) Value { // Get the underlying type, if any. origDstTyp := dstTyp dstTyp = types.Underlying(dstTyp) if !types.Identical(v.typ, dstTyp) { isBasic := false if name, isname := types.Underlying(dstTyp).(*types.Name); isname { _, isBasic = name.Underlying.(*types.Basic) } compiler := v.compiler if isBasic { return ConstValue{v.Const.Convert(&dstTyp), compiler, origDstTyp} } else { return compiler.NewLLVMValue(v.LLVMValue(), v.Type()).Convert(origDstTyp) //panic(fmt.Errorf("unhandled conversion from %v to %v", v.typ, dstTyp)) } } else { v.typ = origDstTyp } return v }
func (v *LLVMValue) chanSend(value Value) { var ptr llvm.Value if value, ok := value.(*LLVMValue); ok && value.pointer != nil { ptr = value.pointer.LLVMValue() } elttyp := types.Underlying(v.typ).(*types.Chan).Elt c := v.compiler if ptr.IsNil() { ptr = c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "") value := value.Convert(elttyp).LLVMValue() c.builder.CreateStore(value, ptr) } uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") f := c.NamedFunction("runtime.chansend", "func f(c, ptr uintptr)") c.builder.CreateCall(f, []llvm.Value{v.LLVMValue(), uintptr_}, "") }
func (c *compiler) VisitMake(expr *ast.CallExpr) Value { typ := c.GetType(expr.Args[0]) switch utyp := types.Underlying(typ).(type) { case *types.Slice: var length, capacity Value switch len(expr.Args) { case 3: capacity = c.VisitExpr(expr.Args[2]) fallthrough case 2: length = c.VisitExpr(expr.Args[1]) } slice := c.makeSlice(utyp.Elt, length, capacity) return c.NewLLVMValue(slice, typ) } // TODO map, chan return c.NewLLVMValue(llvm.ConstNull(c.types.ToLLVM(typ)), typ) }
// loadI2V loads an interface value to a type, without checking // that the interface type matches. func (v *LLVMValue) loadI2V(typ types.Type) Value { c := v.compiler if c.Sizeof(typ) > c.target.PointerSize() { ptr := c.builder.CreateExtractValue(v.LLVMValue(), 1, "") typ = &types.Pointer{Base: typ} ptr = c.builder.CreateBitCast(ptr, c.types.ToLLVM(typ), "") return c.NewLLVMValue(ptr, typ).makePointee() } value := c.builder.CreateExtractValue(v.LLVMValue(), 1, "") if _, ok := types.Underlying(typ).(*types.Pointer); ok { value = c.builder.CreateBitCast(value, c.types.ToLLVM(typ), "") return c.NewLLVMValue(value, typ) } bits := c.target.TypeSizeInBits(c.types.ToLLVM(typ)) value = c.builder.CreatePtrToInt(value, llvm.IntType(int(bits)), "") value = c.coerce(value, c.types.ToLLVM(typ)) return c.NewLLVMValue(value, typ) }
func (v *LLVMValue) Convert(dst_typ types.Type) Value { // If it's a stack allocated value, we'll want to compare the // value type, not the pointer type. src_typ := v.typ // Get the underlying type, if any. orig_dst_typ := dst_typ if name, isname := dst_typ.(*types.Name); isname { dst_typ = types.Underlying(name) } // Get the underlying type, if any. if name, isname := src_typ.(*types.Name); isname { src_typ = types.Underlying(name) } // Identical (underlying) types? Just swap in the destination type. if types.Identical(src_typ, dst_typ) { dst_typ = orig_dst_typ // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } // Convert from an interface type. if _, isinterface := src_typ.(*types.Interface); isinterface { if interface_, isinterface := dst_typ.(*types.Interface); isinterface { return v.convertI2I(interface_) } else { return v.convertI2V(dst_typ) } } // Converting to an interface type. if interface_, isinterface := dst_typ.(*types.Interface); isinterface { return v.convertV2I(interface_) } // string -> []byte byteslice := &types.Slice{Elt: types.Byte} if src_typ == types.String && types.Identical(dst_typ, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") 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.NewLLVMValue(struct_, byteslice) } // []byte -> string if types.Identical(src_typ, byteslice) && dst_typ == types.String { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") struct_ := llvm.Undef(c.types.ToLLVM(types.String)) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewLLVMValue(struct_, types.String) } // TODO other special conversions, e.g. int->string. llvm_type := v.compiler.types.ToLLVM(dst_typ) // Unsafe pointer conversions. if dst_typ == types.UnsafePointer { // X -> unsafe.Pointer if _, isptr := src_typ.(*types.Pointer); isptr { value := v.compiler.builder.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, dst_typ) } else if src_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } } else if src_typ == types.UnsafePointer { // unsafe.Pointer -> X if _, isptr := dst_typ.(*types.Pointer); isptr { value := v.compiler.builder.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, dst_typ) } else if dst_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } } // FIXME select the appropriate cast here, depending on size, type (int/float) // and sign. lv := v.LLVMValue() srcType := lv.Type() switch srcType.TypeKind() { // source type 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 = v.compiler.builder.CreateZExt(lv, llvm_type, "") case delta > 0: lv = v.compiler.builder.CreateTrunc(lv, llvm_type, "") } return v.compiler.NewLLVMValue(lv, dst_typ) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = v.compiler.builder.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, dst_typ) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = v.compiler.builder.CreateFPExt(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, dst_typ) } } //bitcast_value := v.compiler.builder.CreateBitCast(lv, llvm_type, "") /* value_type := value.Type() switch value_type.TypeKind() { case llvm.IntegerTypeKind: switch totype.TypeKind() { case llvm.IntegerTypeKind: //delta := value_type.IntTypeWidth() - totype.IntTypeWidth() //var switch { case delta == 0: return value // TODO handle signed/unsigned (SExt/ZExt) case delta < 0: return c.compiler.builder.CreateZExt(value, totype, "") case delta > 0: return c.compiler.builder.CreateTrunc(value, totype, "") } return LLVMValue{lhs.compiler.builder, value} } } */ panic(fmt.Sprint("unimplemented conversion: ", v.typ, " -> ", orig_dst_typ)) }
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) Value { typ := c.types.expr[lit] var valuemap map[interface{}]Value var valuelist []Value _, isstruct := types.Underlying(typ).(*types.Struct) if lit.Elts != nil { for _, elt := range lit.Elts { var value Value if kv, iskv := elt.(*ast.KeyValueExpr); iskv { value = c.VisitExpr(kv.Value) if valuemap == nil { valuemap = make(map[interface{}]Value) } var key interface{} if isstruct { key = kv.Key.(*ast.Ident).Name } else { key = c.VisitExpr(kv.Key) } valuemap[key] = value } else { value = c.VisitExpr(elt) valuelist = append(valuelist, value) } } } // For array/slice types, convert key:value to contiguous // values initialiser. switch types.Underlying(typ).(type) { case *types.Array, *types.Slice: if len(valuemap) > 0 { maxi := int64(-1) for key, _ := range valuemap { i := key.(ConstValue).Int64() if i < 0 { panic("array index must be non-negative integer constant") } else if i > maxi { maxi = i } } valuelist = make([]Value, maxi+1) for key, value := range valuemap { i := key.(ConstValue).Int64() valuelist[i] = value } } } origtyp := typ switch typ := types.Underlying(typ).(type) { case *types.Array: elttype := typ.Elt llvmelttype := c.types.ToLLVM(elttype) llvmvalues := make([]llvm.Value, typ.Len) for i := range llvmvalues { var value Value if i < len(valuelist) { value = valuelist[i] } if value == nil { llvmvalues[i] = llvm.ConstNull(llvmelttype) } else if _, ok := value.(ConstValue); ok || value.LLVMValue().IsConstant() { llvmvalues[i] = value.Convert(elttype).LLVMValue() } else { llvmvalues[i] = llvm.Undef(llvmelttype) } } array := llvm.ConstArray(llvmelttype, llvmvalues) for i, value := range valuelist { if llvmvalues[i].IsUndef() { value := value.Convert(elttype).LLVMValue() array = c.builder.CreateInsertValue(array, value, i, "") } } return c.NewLLVMValue(array, origtyp) case *types.Slice: ptr := c.createTypeMalloc(c.types.ToLLVM(typ)) eltType := c.types.ToLLVM(typ.Elt) arrayType := llvm.ArrayType(eltType, len(valuelist)) valuesPtr := c.createMalloc(llvm.SizeOf(arrayType)) valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "") //valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "") // TODO check result of mallocs length := llvm.ConstInt(llvm.Int32Type(), uint64(len(valuelist)), false) c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, "")) // len c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, "")) // cap null := llvm.ConstNull(c.types.ToLLVM(typ.Elt)) for i, value := range valuelist { index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "") if value == nil { c.builder.CreateStore(null, valuePtr) } else { c.builder.CreateStore(value.Convert(typ.Elt).LLVMValue(), valuePtr) } } m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp}) return m.makePointee() case *types.Struct: values := valuelist llvmtyp := c.types.ToLLVM(typ) ptr := c.createTypeMalloc(llvmtyp) bzero := c.NamedFunction("runtime.bzero", "func f(unsafe.Pointer, uintptr)") ptrintval := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") args := []llvm.Value{ptrintval, llvm.SizeOf(llvmtyp)} c.builder.CreateCall(bzero, args, "") if valuemap != nil { for key, value := range valuemap { fieldName := key.(string) index := typ.FieldIndices[fieldName] for len(values) <= int(index) { values = append(values, nil) } values[index] = value } } for i, value := range values { if value != nil { elttype := typ.Fields[i].Type.(types.Type) llvm_value := value.Convert(elttype).LLVMValue() ptr := c.builder.CreateStructGEP(ptr, i, "") c.builder.CreateStore(llvm_value, ptr) } } m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp}) return m.makePointee() case *types.Map: value := llvm.ConstNull(c.types.ToLLVM(typ)) // TODO initialise map return c.NewLLVMValue(value, origtyp) } panic(fmt.Sprint("Unhandled type kind: ", typ)) }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { lhs := c.VisitExpr(expr.X) if lhs == nil { // The only time we should get a nil result is if the object is // a package. obj := expr.Sel.Obj if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } return c.Resolve(obj) } // Method expression. Returns an unbound function pointer. // FIXME this is just the most basic case. It's also possible to // create a pointer-receiver function from a method that has a // value receiver (see Method Expressions in spec). name := expr.Sel.Name if _, ok := lhs.(TypeValue); ok { methodobj := expr.Sel.Obj value := c.Resolve(methodobj).(*LLVMValue) ftyp := value.typ.(*types.Func) methodParams := make(types.ObjList, len(ftyp.Params)+1) methodParams[0] = ftyp.Recv copy(methodParams[1:], ftyp.Params) ftyp = &types.Func{ Recv: nil, Params: methodParams, Results: ftyp.Results, IsVariadic: ftyp.IsVariadic, } return c.NewLLVMValue(value.value, ftyp) } // TODO(?) record path to field/method during typechecking, so we don't // have to search again here. if iface, ok := types.Underlying(lhs.Type()).(*types.Interface); ok { i := sort.Search(len(iface.Methods), func(i int) bool { return iface.Methods[i].Name >= name }) structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 1, "") f := c.builder.CreateExtractValue(structValue, i+2, "") i8ptr := &types.Pointer{Base: types.Int8} ftype := iface.Methods[i].Type.(*types.Func) ftype.Recv = ast.NewObj(ast.Var, "") ftype.Recv.Type = i8ptr f = c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), "") ftype.Recv = nil method := c.NewLLVMValue(f, ftype) method.receiver = c.NewLLVMValue(receiver, i8ptr) return method } // Search through embedded types for field/method. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[0:] t := candidate.Type if p, ok := types.Underlying(t).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { t = p.Base } } if n, ok := t.(*types.Name); ok { i := sort.Search(len(n.Methods), func(i int) bool { return n.Methods[i].Name >= name }) if i < len(n.Methods) && n.Methods[i].Name == name { result.Indices = indices result.Type = t } } if t, ok := types.Underlying(t).(*types.Struct); ok { if i, ok := t.FieldIndices[name]; ok { result.Indices = append(indices, int(i)) result.Type = t } else { // Add embedded types to the next set of types // to check. for i, field := range t.Fields { if field.Name == "" { indices = append(indices[0:], i) t := field.Type.(types.Type) candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field/receiver. recvValue := lhs.(*LLVMValue) if len(result.Indices) > 0 { if _, ok := types.Underlying(lhs.Type()).(*types.Pointer); !ok { recvValue = recvValue.pointer //recvValue = c.NewLLVMValue(recvValue.LLVMValue(), recvValue.Type()) } for _, v := range result.Indices { ptr := recvValue.LLVMValue() field := types.Underlying(types.Deref(recvValue.typ)).(*types.Struct).Fields[v] fieldPtr := c.builder.CreateStructGEP(ptr, v, "") fieldPtrTyp := &types.Pointer{Base: field.Type.(types.Type)} recvValue = c.NewLLVMValue(fieldPtr, fieldPtrTyp) // GEP returns a pointer; if the field is a pointer, // we must load our pointer-to-a-pointer. if _, ok := field.Type.(*types.Pointer); ok { recvValue = recvValue.makePointee() } } } // Method? if expr.Sel.Obj.Kind == ast.Fun { method := c.Resolve(expr.Sel.Obj).(*LLVMValue) methodType := expr.Sel.Obj.Type.(*types.Func) receiverType := methodType.Recv.Type.(types.Type) if types.Identical(recvValue.Type(), receiverType) { method.receiver = recvValue } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, receiverType) { method.receiver = recvValue.pointer } else { method.receiver = recvValue.makePointee() } return method } else { resultType := expr.Sel.Obj.Type.(types.Type) if types.Identical(recvValue.Type(), resultType) { // no-op } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, resultType) { recvValue = recvValue.pointer } else { recvValue = recvValue.makePointee() } return recvValue } panic("unreachable") }
func (v *LLVMValue) Convert(dst_typ 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. src_typ := v.typ // Get the underlying type, if any. orig_dst_typ := dst_typ dst_typ = types.Underlying(dst_typ) src_typ = types.Underlying(src_typ) // Identical (underlying) types? Just swap in the destination type. if types.Identical(src_typ, dst_typ) { // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewLLVMValue(v.LLVMValue(), orig_dst_typ) } // Both pointer types with identical underlying types? Same as above. if src_typ, ok := src_typ.(*types.Pointer); ok { if dst_typ, ok := dst_typ.(*types.Pointer); ok { src_typ := types.Underlying(src_typ.Base) dst_typ := types.Underlying(dst_typ.Base) if types.Identical(src_typ, dst_typ) { return v.compiler.NewLLVMValue(v.LLVMValue(), orig_dst_typ) } } } // Convert from an interface type. if _, isinterface := src_typ.(*types.Interface); isinterface { if interface_, isinterface := dst_typ.(*types.Interface); isinterface { result, _ := v.convertI2I(interface_) return result } else { result, _ := v.convertI2V(orig_dst_typ) return result } } // Converting to an interface type. if interface_, isinterface := dst_typ.(*types.Interface); isinterface { return v.convertV2I(interface_) } // string -> []byte byteslice := &types.Slice{Elt: types.Byte} if src_typ == types.String && types.Identical(dst_typ, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") 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.NewLLVMValue(struct_, byteslice) } // []byte -> string if types.Identical(src_typ, byteslice) && dst_typ == types.String { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") struct_ := llvm.Undef(c.types.ToLLVM(types.String)) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewLLVMValue(struct_, types.String) } // Rune to string conversion. if dst_typ == types.String && isIntType(src_typ) { return v.runeToString() } // TODO other special conversions? llvm_type := v.compiler.types.ToLLVM(dst_typ) // Unsafe pointer conversions. if dst_typ == types.UnsafePointer { // X -> unsafe.Pointer if _, isptr := src_typ.(*types.Pointer); isptr { value := b.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, orig_dst_typ) } else if src_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), orig_dst_typ) } } else if src_typ == types.UnsafePointer { // unsafe.Pointer -> X if _, isptr := dst_typ.(*types.Pointer); isptr { value := b.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, orig_dst_typ) } else if dst_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), orig_dst_typ) } } // FIXME select the appropriate cast here, depending on size, type (int/float) // and sign. lv := v.LLVMValue() srcType := lv.Type() switch srcType.TypeKind() { // source type 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.NewLLVMValue(lv, orig_dst_typ) case llvm.FloatTypeKind, llvm.DoubleTypeKind: if signed(v.Type()) { lv = b.CreateSIToFP(lv, llvm_type, "") } else { lv = b.CreateUIToFP(lv, llvm_type, "") } return v.compiler.NewLLVMValue(lv, orig_dst_typ) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = b.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, orig_dst_typ) case llvm.IntegerTypeKind: if signed(dst_typ) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewLLVMValue(lv, orig_dst_typ) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = b.CreateFPExt(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, orig_dst_typ) case llvm.IntegerTypeKind: if signed(dst_typ) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewLLVMValue(lv, orig_dst_typ) } } // 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 src_typ == types.Complex64 || src_typ == types.Complex128 { var fpcast func(llvm.Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if src_typ == types.Complex64 { fpcast = llvm.Builder.CreateFPExt fptype = llvm.DoubleType() } else { fpcast = llvm.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(dst_typ)) lv = b.CreateInsertValue(lv, realv, 0, "") lv = b.CreateInsertValue(lv, imagv, 1, "") return v.compiler.NewLLVMValue(lv, orig_dst_typ) } } panic(fmt.Sprint("unimplemented conversion: ", v.typ, " -> ", orig_dst_typ)) }
func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value { switch x := (expr.Fun).(type) { case *ast.Ident: switch x.String() { case "copy": // TODO zero := llvm.ConstInt(llvm.Int32Type(), 0, false) return c.NewLLVMValue(zero, types.Int) case "print": return c.VisitPrint(expr, false) case "println": return c.VisitPrint(expr, true) case "cap": return c.VisitCap(expr) case "len": return c.VisitLen(expr) case "new": return c.VisitNew(expr) case "make": return c.VisitMake(expr) case "append": return c.VisitAppend(expr) case "delete": m := c.VisitExpr(expr.Args[0]).(*LLVMValue) key := c.VisitExpr(expr.Args[1]) c.mapDelete(m, key) return nil case "panic": var arg Value if len(expr.Args) > 0 { arg = c.VisitExpr(expr.Args[0]) } c.visitPanic(arg) return nil case "recover": return c.visitRecover() case "real": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(0) case "imag": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(1) case "complex": r := c.VisitExpr(expr.Args[0]).LLVMValue() i := c.VisitExpr(expr.Args[1]).LLVMValue() typ := c.types.expr[expr] cmplx := llvm.Undef(c.types.ToLLVM(typ)) cmplx = c.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = c.builder.CreateInsertValue(cmplx, i, 1, "") return c.NewLLVMValue(cmplx, typ) } case *ast.SelectorExpr: // Handle unsafe functions specially. if pkgobj, ok := x.X.(*ast.Ident); ok && pkgobj.Obj.Data == types.Unsafe.Data { var value int switch x.Sel.Name { case "Alignof": argtype := c.types.expr[expr.Args[0]] value = c.alignofType(argtype) value := c.NewConstValue(token.INT, strconv.Itoa(value)) value.typ = types.Uintptr return value case "Sizeof": argtype := c.types.expr[expr.Args[0]] value = c.sizeofType(argtype) value := c.NewConstValue(token.INT, strconv.Itoa(value)) value.typ = types.Uintptr return value case "Offsetof": // FIXME this should be constant, but I'm lazy, and this ought // to be done when we're doing constant folding anyway. lhs := expr.Args[0].(*ast.SelectorExpr).X baseaddr := c.VisitExpr(lhs).(*LLVMValue).pointer.LLVMValue() addr := c.VisitExpr(expr.Args[0]).(*LLVMValue).pointer.LLVMValue() baseaddr = c.builder.CreatePtrToInt(baseaddr, c.target.IntPtrType(), "") addr = c.builder.CreatePtrToInt(addr, c.target.IntPtrType(), "") diff := c.builder.CreateSub(addr, baseaddr, "") return c.NewLLVMValue(diff, types.Uintptr) } } } // Is it a type conversion? if len(expr.Args) == 1 && isType(expr.Fun) { typ := c.types.expr[expr] value := c.VisitExpr(expr.Args[0]) return value.Convert(typ) } // Not a type conversion, so must be a function call. lhs := c.VisitExpr(expr.Fun) fn := lhs.(*LLVMValue) fn_type := types.Underlying(fn.Type()).(*types.Func) args := make([]llvm.Value, 0) if fn.receiver != nil { // Don't dereference the receiver here. It'll have been worked out in // the selector. receiver := fn.receiver args = append(args, receiver.LLVMValue()) } if nparams := len(fn_type.Params); nparams > 0 { if fn_type.IsVariadic { nparams-- } for i := 0; i < nparams; i++ { value := c.VisitExpr(expr.Args[i]) param_type := fn_type.Params[i].Type.(types.Type) args = append(args, value.Convert(param_type).LLVMValue()) } if fn_type.IsVariadic { param_type := fn_type.Params[nparams].Type.(*types.Slice).Elt varargs := make([]llvm.Value, 0) for i := nparams; i < len(expr.Args); i++ { value := c.VisitExpr(expr.Args[i]) value = value.Convert(param_type) varargs = append(varargs, value.LLVMValue()) } slice_value := c.makeLiteralSlice(varargs, param_type) args = append(args, slice_value) } } var result_type types.Type switch len(fn_type.Results) { case 0: // no-op case 1: result_type = fn_type.Results[0].Type.(types.Type) default: fields := make([]*ast.Object, len(fn_type.Results)) for i, result := range fn_type.Results { fields[i] = result } result_type = &types.Struct{Fields: fields} } // After calling the function, we must bitcast to the computed LLVM // type. This is a no-op, and exists just to satisfy LLVM's type // comparisons. result := c.builder.CreateCall(fn.LLVMValue(), args, "") if len(fn_type.Results) == 1 { result = c.builder.CreateBitCast(result, c.types.ToLLVM(result_type), "") } return c.NewLLVMValue(result, result_type) }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { lhs := c.VisitExpr(expr.X) if lhs == nil { // The only time we should get a nil result is if the object is // a package. obj := expr.Sel.Obj if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } return c.Resolve(obj) } // TODO(?) record path to field/method during typechecking, so we don't // have to search again here. name := expr.Sel.Name if iface, ok := types.Underlying(lhs.Type()).(*types.Interface); ok { // validity checked during typechecking. i := sort.Search(len(iface.Methods), func(i int) bool { return iface.Methods[i].Name >= name }) struct_value := lhs.LLVMValue() receiver_value := c.builder.CreateStructGEP(struct_value, 0, "") fn_value := c.builder.CreateStructGEP(struct_value, i+2, "") method_type := c.ObjGetType(iface.Methods[i]).(*types.Func) method := c.NewLLVMValue( c.builder.CreateBitCast( c.builder.CreateLoad(fn_value, ""), c.types.ToLLVM(method_type), ""), method_type) method.receiver = c.NewLLVMValue( c.builder.CreateLoad(receiver_value, ""), method_type.Recv.Type.(types.Type)) return method } // Search through embedded types for field/method. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[0:] t := candidate.Type if p, ok := types.Underlying(t).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { indices = append(indices, 0) t = p.Base } } if n, ok := t.(*types.Name); ok { i := sort.Search(len(n.Methods), func(i int) bool { return n.Methods[i].Name >= name }) if i < len(n.Methods) && n.Methods[i].Name == name { result.Indices = indices result.Type = t } } if t, ok := types.Underlying(t).(*types.Struct); ok { if i, ok := t.FieldIndices[name]; ok { result.Indices = append(indices, i) result.Type = t } else { // Add embedded types to the next set of types // to check. for i, field := range t.Fields { if field.Name == "" { indices = append(indices[0:], uint64(i)) t := field.Type.(types.Type) candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field/struct for field/method invocation. indices := make([]llvm.Value, len(result.Indices)) for i, v := range result.Indices { indices[i] = llvm.ConstInt(llvm.Int32Type(), v, false) } // Method? if expr.Sel.Obj.Kind == ast.Fun { method := c.Resolve(expr.Sel.Obj).(*LLVMValue) methodType := expr.Sel.Obj.Type.(*types.Func) receiverType := methodType.Recv.Type.(types.Type) if len(indices) > 0 { receiverValue := c.builder.CreateGEP(lhs.LLVMValue(), indices, "") if types.Identical(result.Type, receiverType) { receiverValue = c.builder.CreateLoad(receiverValue, "") } method.receiver = c.NewLLVMValue(receiverValue, receiverType) } else { lhs := lhs.(*LLVMValue) if types.Identical(result.Type, receiverType) { method.receiver = lhs } else if types.Identical(result.Type, &types.Pointer{Base: receiverType}) { method.receiver = lhs.makePointee() } else if types.Identical(&types.Pointer{Base: result.Type}, receiverType) { method.receiver = lhs.pointer } } return method } else { var ptr llvm.Value if _, ok := types.Underlying(lhs.Type()).(*types.Pointer); ok { ptr = lhs.LLVMValue() } else { if lhs, ok := lhs.(*LLVMValue); ok && lhs.pointer != nil { ptr = lhs.pointer.LLVMValue() zero := llvm.ConstNull(llvm.Int32Type()) indices = append([]llvm.Value{zero}, indices...) } else { // TODO handle struct literal selectors. panic("selectors on struct literals unimplemented") } } fieldValue := c.builder.CreateGEP(ptr, indices, "") fieldType := &types.Pointer{Base: expr.Sel.Obj.Type.(types.Type)} return c.NewLLVMValue(fieldValue, fieldType).makePointee() } panic("unreachable") }
func (v ConstValue) LLVMValue() llvm.Value { typ := types.Underlying(v.Type()) if name, ok := typ.(*types.Name); ok { typ = name.Underlying } switch typ.(*types.Basic).Kind { case types.IntKind, types.UintKind: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), true) // TODO 32/64bit (probably wait for gc) //int_val := v.Val.(*big.Int) //if int_val.Cmp(maxBigInt32) > 0 || int_val.Cmp(minBigInt32) < 0 { // panic(fmt.Sprint("const ", int_val, " overflows int")) //} //return llvm.ConstInt(v.compiler.target.IntPtrType(), uint64(v.Int64()), true) case types.Int8Kind: return llvm.ConstInt(llvm.Int8Type(), uint64(v.Int64()), true) case types.Uint8Kind: return llvm.ConstInt(llvm.Int8Type(), uint64(v.Int64()), false) case types.Int16Kind: return llvm.ConstInt(llvm.Int16Type(), uint64(v.Int64()), true) case types.Uint16Kind: return llvm.ConstInt(llvm.Int16Type(), uint64(v.Int64()), false) case types.Int32Kind: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), true) case types.Uint32Kind: return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), false) case types.Int64Kind: return llvm.ConstInt(llvm.Int64Type(), uint64(v.Int64()), true) case types.Uint64Kind: return llvm.ConstInt(llvm.Int64Type(), uint64(v.Int64()), false) case types.Float32Kind: return llvm.ConstFloat(llvm.FloatType(), float64(v.Float64())) case types.Float64Kind: return llvm.ConstFloat(llvm.DoubleType(), float64(v.Float64())) case types.Complex64Kind: r_, i_ := v.Complex() r := llvm.ConstFloat(llvm.FloatType(), r_) i := llvm.ConstFloat(llvm.FloatType(), i_) return llvm.ConstStruct([]llvm.Value{r, i}, false) case types.Complex128Kind: r_, i_ := v.Complex() r := llvm.ConstFloat(llvm.DoubleType(), r_) i := llvm.ConstFloat(llvm.DoubleType(), i_) return llvm.ConstStruct([]llvm.Value{r, i}, false) case types.UnsafePointerKind, types.UintptrKind: inttype := v.compiler.target.IntPtrType() return llvm.ConstInt(inttype, uint64(v.Int64()), false) case types.StringKind: strval := (v.Val).(string) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { ptr = v.compiler.builder.CreateGlobalStringPtr(strval, "") ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(llvm.Int32Type(), uint64(strlen), false) return llvm.ConstStruct([]llvm.Value{ptr, len_}, false) case types.BoolKind: if v := v.Val.(bool); v { return llvm.ConstAllOnes(llvm.Int1Type()) } return llvm.ConstNull(llvm.Int1Type()) } panic(fmt.Errorf("Unhandled type: %v", typ)) //v.typ.Kind)) }
func (c *compiler) VisitRangeStmt(stmt *ast.RangeStmt) { currBlock := c.builder.GetInsertBlock() doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done") doneBlock.MoveAfter(currBlock) postBlock := llvm.InsertBasicBlock(doneBlock, "post") loopBlock := llvm.InsertBasicBlock(postBlock, "loop") condBlock := llvm.InsertBasicBlock(loopBlock, "cond") defer c.builder.SetInsertPointAtEnd(doneBlock) // Evaluate range expression first. x := c.VisitExpr(stmt.X) // If it's a pointer type, we'll first check that it's non-nil. typ := types.Underlying(x.Type()) if _, ok := typ.(*types.Pointer); ok { ifBlock := llvm.InsertBasicBlock(doneBlock, "if") isnotnull := c.builder.CreateIsNotNull(x.LLVMValue(), "") c.builder.CreateCondBr(isnotnull, ifBlock, doneBlock) c.builder.SetInsertPointAtEnd(ifBlock) } // Is it a new var definition? Then allocate some memory on the stack. var keyType, valueType types.Type var keyPtr, valuePtr llvm.Value if stmt.Tok == token.DEFINE { if key := stmt.Key.(*ast.Ident); key.Name != "_" { keyType = key.Obj.Type.(types.Type) keyPtr = c.builder.CreateAlloca(c.types.ToLLVM(keyType), "") key.Obj.Data = c.NewLLVMValue(keyPtr, &types.Pointer{Base: keyType}).makePointee() } if stmt.Value != nil { if value := stmt.Value.(*ast.Ident); value.Name != "_" { valueType = value.Obj.Type.(types.Type) valuePtr = c.builder.CreateAlloca(c.types.ToLLVM(valueType), "") value.Obj.Data = c.NewLLVMValue(valuePtr, &types.Pointer{Base: valueType}).makePointee() } } } c.breakblocks = append(c.breakblocks, doneBlock) c.continueblocks = append(c.continueblocks, postBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] c.continueblocks = c.continueblocks[:len(c.continueblocks)-1] }() isarray := false var base, length llvm.Value _, isptr := typ.(*types.Pointer) if isptr { typ = typ.(*types.Pointer).Base } switch typ := types.Underlying(typ).(type) { case *types.Map: goto maprange case *types.Name: stringvalue := x.LLVMValue() length = c.builder.CreateExtractValue(stringvalue, 1, "") goto stringrange case *types.Array: isarray = true x := x if !isptr { if x_, ok := x.(*LLVMValue); ok && x_.pointer != nil { x = x_.pointer } else { // TODO load value onto stack for indexing? } } base = x.LLVMValue() length = llvm.ConstInt(llvm.Int32Type(), typ.Len, false) goto arrayrange case *types.Slice: slicevalue := x.LLVMValue() base = c.builder.CreateExtractValue(slicevalue, 0, "") length = c.builder.CreateExtractValue(slicevalue, 1, "") goto arrayrange } maprange: { currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) nextptrphi := c.builder.CreatePHI(c.target.IntPtrType(), "next") nextptr, pk, pv := c.mapNext(x.(*LLVMValue), nextptrphi) notnull := c.builder.CreateIsNotNull(nextptr, "") c.builder.CreateCondBr(notnull, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { keyval := c.builder.CreateLoad(pk, "") c.builder.CreateStore(keyval, keyPtr) } if !valuePtr.IsNil() { valval := c.builder.CreateLoad(pv, "") c.builder.CreateStore(valval, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) c.builder.CreateBr(condBlock) nextptrphi.AddIncoming([]llvm.Value{llvm.ConstNull(c.target.IntPtrType()), nextptr}, []llvm.BasicBlock{currBlock, postBlock}) return } stringrange: { zero := llvm.ConstNull(llvm.Int32Type()) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(llvm.Int32Type(), "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) consumed, value := c.stringNext(x.LLVMValue(), index) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { c.builder.CreateStore(value, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, consumed, "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } arrayrange: { zero := llvm.ConstNull(llvm.Int32Type()) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(llvm.Int32Type(), "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { var indices []llvm.Value if isarray { indices = []llvm.Value{zero, index} } else { indices = []llvm.Value{index} } elementptr := c.builder.CreateGEP(base, indices, "") element := c.builder.CreateLoad(elementptr, "") c.builder.CreateStore(element, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, llvm.ConstInt(llvm.Int32Type(), 1, false), "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } }
func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value { switch x := (expr.Fun).(type) { case *ast.Ident: switch x.String() { case "print": return c.VisitPrint(expr, false) case "println": return c.VisitPrint(expr, true) case "len": return c.VisitLen(expr) case "new": return c.VisitNew(expr) case "make": return c.VisitMake(expr) case "append": return c.VisitAppend(expr) case "delete": m := c.VisitExpr(expr.Args[0]).(*LLVMValue) key := c.VisitExpr(expr.Args[1]) c.mapDelete(m, key) return nil case "panic": // TODO return nil } case *ast.SelectorExpr: // Handle unsafe functions specially. if pkgobj, ok := x.X.(*ast.Ident); ok && pkgobj.Obj.Data == types.Unsafe.Data { var value int switch x.Sel.Name { case "Alignof", "Offsetof": panic("unimplemented") case "Sizeof": argtype := c.types.expr[expr.Args[0]] value = c.sizeofType(argtype) value := c.NewConstValue(token.INT, strconv.Itoa(value)) value.typ = types.Uintptr return value } } } lhs := c.VisitExpr(expr.Fun) // Is it a type conversion? if len(expr.Args) == 1 { if _, ok := lhs.(TypeValue); ok { typ := lhs.Type() value := c.VisitExpr(expr.Args[0]) return value.Convert(typ) } } // Not a type conversion, so must be a function call. fn := lhs.(*LLVMValue) fn_type := types.Underlying(fn.Type()).(*types.Func) args := make([]llvm.Value, 0) if fn_type.Recv != nil { // Don't dereference the receiver here. It'll have been worked out in // the selector. receiver := fn.receiver args = append(args, receiver.LLVMValue()) } if nparams := len(fn_type.Params); nparams > 0 { if fn_type.IsVariadic { nparams-- } for i := 0; i < nparams; i++ { value := c.VisitExpr(expr.Args[i]) param_type := fn_type.Params[i].Type.(types.Type) args = append(args, value.Convert(param_type).LLVMValue()) } if fn_type.IsVariadic { param_type := fn_type.Params[nparams].Type.(*types.Slice).Elt varargs := make([]llvm.Value, 0) for i := nparams; i < len(expr.Args); i++ { value := c.VisitExpr(expr.Args[i]) value = value.Convert(param_type) varargs = append(varargs, value.LLVMValue()) } slice_value := c.makeLiteralSlice(varargs, param_type) args = append(args, slice_value) } } var result_type types.Type switch len(fn_type.Results) { case 0: // no-op case 1: result_type = fn_type.Results[0].Type.(types.Type) default: fields := make([]*ast.Object, len(fn_type.Results)) for i, result := range fn_type.Results { fields[i] = result } result_type = &types.Struct{Fields: fields} } // After calling the function, we must bitcast to the computed LLVM // type. This is a no-op, and exists just to satisfy LLVM's type // comparisons. result := c.builder.CreateCall(fn.LLVMValue(), args, "") if len(fn_type.Results) == 1 { result = c.builder.CreateBitCast(result, c.types.ToLLVM(result_type), "") } return c.NewLLVMValue(result, result_type) }
// convertV2I converts a value to an interface. func (v *LLVMValue) convertV2I(iface *types.Interface) Value { // TODO deref indirect value, then use 'pointer' as pointer value. var srcname *types.Name srctyp := v.Type() if name, isname := srctyp.(*types.Name); isname { srcname = name srctyp = name.Underlying } if p, fromptr := srctyp.(*types.Pointer); fromptr { srctyp = p.Base if name, isname := srctyp.(*types.Name); isname { srcname = name srctyp = name.Underlying } } iface_struct_type := v.compiler.types.ToLLVM(iface) element_types := iface_struct_type.StructElementTypes() zero_iface_struct := llvm.ConstNull(iface_struct_type) iface_struct := zero_iface_struct builder := v.compiler.builder var ptr llvm.Value lv := v.LLVMValue() var overwide bool if lv.Type().TypeKind() == llvm.PointerTypeKind { ptr = builder.CreateBitCast(lv, element_types[1], "") } else { // If the value fits exactly in a pointer, then we can just // bitcast it. Otherwise we need to malloc, and create a shim // function to load the receiver. c := v.compiler ptrsize := c.target.PointerSize() if c.target.TypeStoreSize(lv.Type()) <= uint64(ptrsize) { bits := c.target.TypeSizeInBits(lv.Type()) if bits > 0 { lv = c.coerce(lv, llvm.IntType(int(bits))) ptr = builder.CreateIntToPtr(lv, element_types[1], "") } else { ptr = llvm.ConstNull(element_types[1]) } } else { ptr = c.createTypeMalloc(v.compiler.types.ToLLVM(srctyp)) builder.CreateStore(lv, ptr) ptr = builder.CreateBitCast(ptr, element_types[1], "") overwide = true } } runtimeType := v.compiler.types.ToRuntime(v.Type()) runtimeType = builder.CreateBitCast(runtimeType, element_types[0], "") iface_struct = builder.CreateInsertValue(iface_struct, runtimeType, 0, "") iface_struct = builder.CreateInsertValue(iface_struct, ptr, 1, "") // TODO assert either source is a named type (or pointer to), or the // interface has an empty methodset. if srcname != nil { // TODO check whether the functions in the struct take // value or pointer receivers. // Look up the method by name. // TODO check embedded types. for i, m := range iface.Methods { // TODO make this loop linear by iterating through the // interface methods and type methods together. var methodobj *ast.Object curr := []types.Type{srcname} for methodobj == nil && len(curr) > 0 { var next []types.Type for _, typ := range curr { if p, ok := types.Underlying(typ).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { typ = p.Base } } if n, ok := typ.(*types.Name); ok { methods := n.Methods mi := sort.Search(len(methods), func(i int) bool { return methods[i].Name >= m.Name }) if mi < len(methods) && methods[mi].Name == m.Name { methodobj = methods[mi] break } } if typ, ok := types.Underlying(typ).(*types.Struct); ok { for _, field := range typ.Fields { if field.Name == "" { typ := field.Type.(types.Type) next = append(next, typ) } } } } curr = next } if methodobj == nil { msg := fmt.Sprintf("Failed to locate (%s).%s", srcname.Obj.Name, m.Name) panic(msg) } method := v.compiler.Resolve(methodobj).(*LLVMValue) llvm_value := method.LLVMValue() // If we have a receiver wider than a word, or a pointer // receiver value and non-pointer receiver method, then // we must use the "wrapper" pointer method. fntyp := methodobj.Type.(*types.Func) recvtyp := fntyp.Recv.Type.(types.Type) needload := overwide if !needload { // TODO handle embedded types here. //needload = types.Identical(v.Type(), recvtyp) if p, ok := v.Type().(*types.Pointer); ok { needload = types.Identical(p.Base, recvtyp) } } if needload { ifname := fmt.Sprintf("*%s.%s", recvtyp, methodobj.Name) llvm_value = v.compiler.module.NamedFunction(ifname) } llvm_value = builder.CreateBitCast(llvm_value, element_types[i+2], "") iface_struct = builder.CreateInsertValue(iface_struct, llvm_value, i+2, "") } } return v.compiler.NewLLVMValue(iface_struct, iface) }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { lhs := c.VisitExpr(expr.X) if lhs == nil { // The only time we should get a nil result is if the object is // a package. obj := expr.Sel.Obj if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } return c.Resolve(obj) } // TODO(?) record path to field/method during typechecking, so we don't // have to search again here. name := expr.Sel.Name if iface, ok := types.Underlying(lhs.Type()).(*types.Interface); ok { i := sort.Search(len(iface.Methods), func(i int) bool { return iface.Methods[i].Name >= name }) structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 0, "") f := c.builder.CreateExtractValue(structValue, i+2, "") ftype := c.ObjGetType(iface.Methods[i]).(*types.Func) method := c.NewLLVMValue(c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), ""), ftype) method.receiver = c.NewLLVMValue(receiver, ftype.Recv.Type.(types.Type)) return method } // Search through embedded types for field/method. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[0:] t := candidate.Type if p, ok := types.Underlying(t).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { t = p.Base } } if n, ok := t.(*types.Name); ok { i := sort.Search(len(n.Methods), func(i int) bool { return n.Methods[i].Name >= name }) if i < len(n.Methods) && n.Methods[i].Name == name { result.Indices = indices result.Type = t } } if t, ok := types.Underlying(t).(*types.Struct); ok { if i, ok := t.FieldIndices[name]; ok { result.Indices = append(indices, int(i)) result.Type = t } else { // Add embedded types to the next set of types // to check. for i, field := range t.Fields { if field.Name == "" { indices = append(indices[0:], i) t := field.Type.(types.Type) candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field/receiver. recvValue := lhs.(*LLVMValue) if _, ok := types.Underlying(lhs.Type()).(*types.Pointer); !ok { recvValue = recvValue.pointer } recvValue = c.NewLLVMValue(recvValue.LLVMValue(), recvValue.Type()) if len(result.Indices) > 0 { for _, v := range result.Indices { ptr := recvValue.LLVMValue() field := types.Underlying(types.Deref(recvValue.typ)).(*types.Struct).Fields[v] fieldPtr := c.builder.CreateStructGEP(ptr, v, "") fieldPtrTyp := &types.Pointer{Base: field.Type.(types.Type)} recvValue = c.NewLLVMValue(fieldPtr, fieldPtrTyp) // GEP returns a pointer; if the field is a pointer, // we must load our pointer-to-a-pointer. if _, ok := field.Type.(*types.Pointer); ok { recvValue = recvValue.makePointee() } } } if !types.Identical(recvValue.typ, expr.Sel.Obj.Type.(types.Type)) { recvValue = recvValue.makePointee() } // Method? if expr.Sel.Obj.Kind == ast.Fun { method := c.Resolve(expr.Sel.Obj).(*LLVMValue) methodType := expr.Sel.Obj.Type.(*types.Func) receiverType := methodType.Recv.Type.(types.Type) if types.Identical(recvValue.Type(), receiverType) { method.receiver = recvValue } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, receiverType) { method.receiver = recvValue.pointer } else { method.receiver = recvValue.makePointee() } return method } else { return recvValue } panic("unreachable") }
func (lhs *LLVMValue) BinaryOp(op token.Token, rhs_ Value) Value { if op == token.NEQ { result := lhs.BinaryOp(token.EQL, rhs_) return result.UnaryOp(token.NOT) } var result llvm.Value c := lhs.compiler b := lhs.compiler.builder // Later we can do better by treating constants specially. For now, let's // convert to LLVMValue's. var rhs *LLVMValue rhsisnil := false switch rhs_ := rhs_.(type) { case *LLVMValue: rhs = rhs_ case NilValue: rhsisnil = true switch rhs_ := rhs_.Convert(lhs.Type()).(type) { case ConstValue: rhs = c.NewLLVMValue(rhs_.LLVMValue(), rhs_.Type()) case *LLVMValue: rhs = rhs_ } case ConstValue: value := rhs_.Convert(lhs.Type()) rhs = c.NewLLVMValue(value.LLVMValue(), value.Type()) } switch typ := types.Underlying(lhs.typ).(type) { case *types.Struct: // TODO check types are the same. element_types_count := lhs.LLVMValue().Type().StructElementTypesCount() struct_fields := typ.Fields if element_types_count > 0 { t := c.ObjGetType(struct_fields[0]) first_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), t) first_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), 0, ""), t) first := first_lhs.BinaryOp(op, first_rhs) logical_op := token.LAND if op == token.NEQ { logical_op = token.LOR } result := first for i := 1; i < element_types_count; i++ { t := c.ObjGetType(struct_fields[i]) next_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) next_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) next := next_lhs.BinaryOp(op, next_rhs) result = result.BinaryOp(logical_op, next) } return result } case *types.Interface: if rhsisnil { valueNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result := b.CreateAnd(typeNull, valueNull, "") return c.NewLLVMValue(result, types.Bool) } // TODO check for interface/interface comparison vs. interface/value comparison. return lhs.compareI2I(rhs) case *types.Slice: // []T == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewLLVMValue(isnil, types.Bool) } // Strings. if types.Underlying(lhs.typ) == types.String { if types.Underlying(rhs.typ) == types.String { switch op { case token.ADD: return c.concatenateStrings(lhs, rhs) case token.EQL, token.LSS, token.GTR, token.LEQ, token.GEQ: return c.compareStrings(lhs, rhs, op) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } } panic("unimplemented") } // Determine whether to use integer or floating point instructions. // TODO determine the NaN rules. isfp := types.Identical(types.Underlying(lhs.typ), types.Float32) || types.Identical(types.Underlying(lhs.typ), types.Float64) switch op { case token.MUL: if isfp { result = b.CreateFMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.QUO: if isfp { result = b.CreateFDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.REM: if isfp { result = b.CreateFRem(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateURem(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.ADD: if isfp { result = b.CreateFAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SUB: if isfp { result = b.CreateFSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SHL: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) result = b.CreateShl(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SHR: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) result = b.CreateAShr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.NEQ: if isfp { result = b.CreateFCmp(llvm.FloatONE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntNE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.EQL: if isfp { result = b.CreateFCmp(llvm.FloatOEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LSS: if isfp { result = b.CreateFCmp(llvm.FloatOLT, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LEQ: // TODO signed/unsigned if isfp { result = b.CreateFCmp(llvm.FloatOLE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GTR: if isfp { result = b.CreateFCmp(llvm.FloatOGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GEQ: if isfp { result = b.CreateFCmp(llvm.FloatOGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.AND: // a & b result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.OR: // a | b result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.XOR: // a ^ b result = b.CreateXor(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } panic("unreachable") }