// mapLookup searches a map for a specified key, returning a pointer to the // memory location for the value. If insert is given as true, and the key // does not exist in the map, it will be added with an uninitialised value. func (c *compiler) mapLookup(m *LLVMValue, key Value, insert bool) (elem *LLVMValue, notnull *LLVMValue) { mapType := m.Type().Underlying().(*types.Map) maplookup := c.NamedFunction("runtime.maplookup", "func f(t, m, k uintptr, insert bool) uintptr") ptrType := c.target.IntPtrType() args := make([]llvm.Value, 4) args[0] = llvm.ConstPtrToInt(c.types.ToRuntime(mapType), ptrType) args[1] = c.builder.CreatePtrToInt(m.LLVMValue(), ptrType, "") if insert { args[3] = llvm.ConstAllOnes(llvm.Int1Type()) } else { args[3] = llvm.ConstNull(llvm.Int1Type()) } if lv, islv := key.(*LLVMValue); islv && lv.pointer != nil { args[2] = c.builder.CreatePtrToInt(lv.pointer.LLVMValue(), ptrType, "") } if args[2].IsNil() { stackval := c.builder.CreateAlloca(c.types.ToLLVM(key.Type()), "") c.builder.CreateStore(key.LLVMValue(), stackval) args[2] = c.builder.CreatePtrToInt(stackval, ptrType, "") } eltPtrType := types.NewPointer(mapType.Elem()) llvmtyp := c.types.ToLLVM(eltPtrType) zeroglobal := llvm.AddGlobal(c.module.Module, llvmtyp.ElementType(), "") zeroglobal.SetInitializer(llvm.ConstNull(llvmtyp.ElementType())) result := c.builder.CreateCall(maplookup, args, "") result = c.builder.CreateIntToPtr(result, llvmtyp, "") notnull_ := c.builder.CreateIsNotNull(result, "") result = c.builder.CreateSelect(notnull_, result, zeroglobal, "") value := c.NewValue(result, eltPtrType) return value.makePointee(), c.NewValue(notnull_, types.Typ[types.Bool]) }
// convertI2V converts an interface to a value. func (v *LLVMValue) convertI2V(typ types.Type) (result, success Value) { builder := v.compiler.builder predicate := v.interfaceTypeEquals(typ).LLVMValue() // If result is zero, then we've got a match. end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") builder.CreateCondBr(predicate, match, nonmatch) builder.SetInsertPointAtEnd(match) matchResultValue := v.loadI2V(typ).LLVMValue() builder.CreateBr(end) builder.SetInsertPointAtEnd(nonmatch) nonmatchResultValue := llvm.ConstNull(matchResultValue.Type()) builder.CreateBr(end) builder.SetInsertPointAtEnd(end) successValue := builder.CreatePHI(llvm.Int1Type(), "") resultValue := builder.CreatePHI(matchResultValue.Type(), "") successValues := []llvm.Value{llvm.ConstAllOnes(llvm.Int1Type()), llvm.ConstNull(llvm.Int1Type())} successBlocks := []llvm.BasicBlock{match, nonmatch} successValue.AddIncoming(successValues, successBlocks) success = v.compiler.NewValue(successValue, types.Typ[types.Bool]) resultValues := []llvm.Value{matchResultValue, nonmatchResultValue} resultBlocks := []llvm.BasicBlock{match, nonmatch} resultValue.AddIncoming(resultValues, resultBlocks) result = v.compiler.NewValue(resultValue, typ) return result, success }
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 (v *LLVMValue) UnaryOp(op token.Token) Value { b := v.compiler.builder switch op { case token.SUB: var value llvm.Value if isFloat(v.typ) { zero := llvm.ConstNull(v.compiler.types.ToLLVM(v.Type())) value = b.CreateFSub(zero, v.LLVMValue(), "") } else { value = b.CreateNeg(v.LLVMValue(), "") } return v.compiler.NewValue(value, v.typ) case token.ADD: return v // No-op case token.NOT: value := b.CreateNot(v.LLVMValue(), "") return v.compiler.NewValue(value, v.typ) case token.XOR: lhs := v.LLVMValue() rhs := llvm.ConstAllOnes(lhs.Type()) value := b.CreateXor(lhs, rhs, "") return v.compiler.NewValue(value, v.typ) default: panic(fmt.Sprintf("Unhandled operator: %s", op)) } panic("unreachable") }
// Binary logical operators are handled specially, outside of the Value // type, because of the need to perform lazy evaluation. // // Binary logical operators are implemented using a Phi node, which takes // on the appropriate value depending on which basic blocks branch to it. func (c *compiler) compileLogicalOp(op token.Token, lhs Value, rhsFunc func() Value) Value { lhsBlock := c.builder.GetInsertBlock() resultBlock := llvm.AddBasicBlock(lhsBlock.Parent(), "") resultBlock.MoveAfter(lhsBlock) rhsBlock := llvm.InsertBasicBlock(resultBlock, "") falseBlock := llvm.InsertBasicBlock(resultBlock, "") if op == token.LOR { c.builder.CreateCondBr(lhs.LLVMValue(), resultBlock, rhsBlock) } else { c.builder.CreateCondBr(lhs.LLVMValue(), rhsBlock, falseBlock) } c.builder.SetInsertPointAtEnd(rhsBlock) rhs := rhsFunc() rhsBlock = c.builder.GetInsertBlock() // rhsFunc may create blocks c.builder.CreateCondBr(rhs.LLVMValue(), resultBlock, falseBlock) c.builder.SetInsertPointAtEnd(falseBlock) c.builder.CreateBr(resultBlock) c.builder.SetInsertPointAtEnd(resultBlock) result := c.builder.CreatePHI(llvm.Int1Type(), "") trueValue := llvm.ConstAllOnes(llvm.Int1Type()) falseValue := llvm.ConstNull(llvm.Int1Type()) var values []llvm.Value var blocks []llvm.BasicBlock if op == token.LOR { values = []llvm.Value{trueValue, trueValue, falseValue} blocks = []llvm.BasicBlock{lhsBlock, rhsBlock, falseBlock} } else { values = []llvm.Value{trueValue, falseValue} blocks = []llvm.BasicBlock{rhsBlock, falseBlock} } result.AddIncoming(values, blocks) return c.NewLLVMValue(result, types.Bool) }
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 boolLLVMValue(v bool) (lv llvm.Value) { if v { lv = llvm.ConstAllOnes(llvm.Int1Type()) } else { lv = llvm.ConstNull(llvm.Int1Type()) } return lv }
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 (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 (lhs *LLVMValue) shift(rhs *LLVMValue, op token.Token) *LLVMValue { rhs = rhs.Convert(lhs.Type()).(*LLVMValue) lhsval := lhs.LLVMValue() bits := rhs.LLVMValue() unsigned := isUnsigned(lhs.Type()) if !bits.IsAConstant().IsNil() { if bits.ZExtValue() >= uint64(lhsval.Type().IntTypeWidth()) { var fix llvm.Value if unsigned || op == token.SHL { fix = llvm.ConstNull(lhsval.Type()) } else { fix = llvm.ConstAllOnes(lhsval.Type()) } return lhs.compiler.NewValue(fix, lhs.typ) } } b := lhs.compiler.builder var result llvm.Value if op == token.SHL { result = b.CreateShl(lhsval, bits, "") } else { if unsigned { result = b.CreateLShr(lhsval, bits, "") } else { result = b.CreateAShr(lhsval, bits, "") } } if bits.IsAConstant().IsNil() { // Shifting >= the width of the lhs // yields undefined behaviour, so we // must generate runtime branching logic. width := llvm.ConstInt(bits.Type(), uint64(lhsval.Type().IntTypeWidth()), false) less := b.CreateICmp(llvm.IntULT, bits, width, "") var fix llvm.Value if unsigned || op == token.SHL { fix = llvm.ConstNull(lhsval.Type()) } else { fix = llvm.ConstAllOnes(lhsval.Type()) } result = b.CreateSelect(less, result, fix, "") } return lhs.compiler.NewValue(result, lhs.typ) }
func (v *LLVMValue) UnaryOp(op token.Token) Value { b := v.compiler.builder switch op { case token.SUB: var value llvm.Value if isFloat(v.typ) { zero := llvm.ConstNull(v.compiler.types.ToLLVM(v.Type())) value = b.CreateFSub(zero, v.LLVMValue(), "") } else { value = b.CreateNeg(v.LLVMValue(), "") } return v.compiler.NewValue(value, v.typ) case token.ADD: return v // No-op case token.AND: if typ, ok := v.typ.Underlying().(*types.Pointer); ok { if typ.Elem().Underlying() == typ { // Taking the address of a recursive pointer // yields a value with the same type. value := v.pointer.value basetyp := value.Type().ElementType() value = b.CreateBitCast(value, basetyp, "") return v.compiler.NewValue(value, v.typ) } } return v.pointer case token.NOT: value := b.CreateNot(v.LLVMValue(), "") return v.compiler.NewValue(value, v.typ) case token.XOR: lhs := v.LLVMValue() rhs := llvm.ConstAllOnes(lhs.Type()) value := b.CreateXor(lhs, rhs, "") return v.compiler.NewValue(value, v.typ) case token.ARROW: value, _ := v.chanRecv(false) return value default: panic(fmt.Sprintf("Unhandled operator: %s", op)) } panic("unreachable") }
// convertI2V converts an interface to a value. func (v *LLVMValue) convertI2V(typ types.Type) (result, success Value) { typptrType := llvm.PointerType(llvm.Int8Type(), 0) runtimeType := v.compiler.types.ToRuntime(typ) runtimeType = llvm.ConstBitCast(runtimeType, typptrType) vval := v.LLVMValue() builder := v.compiler.builder ifaceType := builder.CreateExtractValue(vval, 0, "") diff := builder.CreatePtrDiff(runtimeType, ifaceType, "") zero := llvm.ConstNull(diff.Type()) predicate := builder.CreateICmp(llvm.IntEQ, diff, zero, "") // If result is zero, then we've got a match. end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") builder.CreateCondBr(predicate, match, nonmatch) builder.SetInsertPointAtEnd(match) matchResultValue := v.loadI2V(typ).LLVMValue() builder.CreateBr(end) builder.SetInsertPointAtEnd(nonmatch) nonmatchResultValue := llvm.ConstNull(matchResultValue.Type()) builder.CreateBr(end) builder.SetInsertPointAtEnd(end) successValue := builder.CreatePHI(llvm.Int1Type(), "") resultValue := builder.CreatePHI(matchResultValue.Type(), "") successValues := []llvm.Value{llvm.ConstAllOnes(llvm.Int1Type()), llvm.ConstNull(llvm.Int1Type())} successBlocks := []llvm.BasicBlock{match, nonmatch} successValue.AddIncoming(successValues, successBlocks) success = v.compiler.NewLLVMValue(successValue, types.Bool) resultValues := []llvm.Value{matchResultValue, nonmatchResultValue} resultBlocks := []llvm.BasicBlock{match, nonmatch} resultValue.AddIncoming(resultValues, resultBlocks) result = v.compiler.NewLLVMValue(resultValue, typ) return result, success }
func (d *SubprogramDescriptor) mdNode(info *DebugInfo) llvm.Value { return llvm.MDNode([]llvm.Value{ llvm.ConstInt(llvm.Int32Type(), llvm.LLVMDebugVersion+uint64(d.Tag()), false), FileDescriptor(d.File).path(), info.MDNode(d.Context), llvm.MDString(d.Name), llvm.MDString(d.DisplayName), llvm.MDString(""), // mips linkage name llvm.ConstInt(llvm.Int32Type(), uint64(d.Line), false), info.MDNode(d.Type), llvm.ConstNull(llvm.Int1Type()), // not static llvm.ConstAllOnes(llvm.Int1Type()), // locally defined (not extern) llvm.ConstNull(llvm.Int32Type()), // virtuality llvm.ConstNull(llvm.Int32Type()), // index into a virtual function info.MDNode(nil), // basetype containing the vtable pointer llvm.ConstInt(llvm.Int32Type(), 0, false), // flags llvm.ConstNull(llvm.Int1Type()), // not optimised d.Function, info.MDNode(nil), // Template parameters info.MDNode(nil), // function declaration descriptor llvm.MDNode(nil), // function variables llvm.ConstInt(llvm.Int32Type(), uint64(d.ScopeLine), false), // Line number where the scope of the subprogram begins }) }
func (c *compiler) NewConstValue(v exact.Value, typ types.Type) *LLVMValue { switch { case v.Kind() == exact.Unknown: // TODO nil literals should be represented more appropriately once the exact-package supports it. 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|rune) if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) { 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 (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 // We can avoid various runtime calls by treating // nil specially. For all nil-able types, nil is // equivalent to the zero value. rhs := rhs_.(*LLVMValue) rhsisnil := rhs.pointer == nil && rhs.LLVMValue().IsNull() switch typ := lhs.typ.Underlying().(type) { case *types.Struct: element_types_count := lhs.LLVMValue().Type().StructElementTypesCount() if element_types_count > 0 { t := typ.Field(0).Type() first_lhs := c.NewValue(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), t) first_rhs := c.NewValue(b.CreateExtractValue(rhs.LLVMValue(), 0, ""), t) first := first_lhs.BinaryOp(op, first_rhs) result := first for i := 1; i < element_types_count; i++ { result = c.compileLogicalOp(token.LAND, result, func() Value { t := typ.Field(i).Type() lhs := c.NewValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) rhs := c.NewValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) return lhs.BinaryOp(op, rhs) }) } return result } case *types.Interface: if rhsisnil { typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") valueNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result := b.CreateAnd(typeNull, valueNull, "") return c.NewValue(result, types.Typ[types.Bool]) } if _, ok := rhs.typ.Underlying().(*types.Interface); ok { return lhs.compareI2I(rhs) } return lhs.compareI2V(rhs) case *types.Slice: // []T == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) case *types.Signature: // func == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) } // Strings. if isString(lhs.typ) { if isString(rhs.typ) { 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") } // Complex numbers. if isComplex(lhs.typ) { // XXX Should we represent complex numbers as vectors? lhsval := lhs.LLVMValue() rhsval := rhs.LLVMValue() a_ := b.CreateExtractValue(lhsval, 0, "") b_ := b.CreateExtractValue(lhsval, 1, "") c_ := b.CreateExtractValue(rhsval, 0, "") d_ := b.CreateExtractValue(rhsval, 1, "") switch op { case token.QUO: // (a+bi)/(c+di) = (ac+bd)/(c**2+d**2) + (bc-ad)/(c**2+d**2)i ac := b.CreateFMul(a_, c_, "") bd := b.CreateFMul(b_, d_, "") bc := b.CreateFMul(b_, c_, "") ad := b.CreateFMul(a_, d_, "") cpow2 := b.CreateFMul(c_, c_, "") dpow2 := b.CreateFMul(d_, d_, "") denom := b.CreateFAdd(cpow2, dpow2, "") realnumer := b.CreateFAdd(ac, bd, "") imagnumer := b.CreateFSub(bc, ad, "") real_ := b.CreateFDiv(realnumer, denom, "") imag_ := b.CreateFDiv(imagnumer, denom, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.MUL: // (a+bi)(c+di) = (ac-bd)+(bc+ad)i ac := b.CreateFMul(a_, c_, "") bd := b.CreateFMul(b_, d_, "") bc := b.CreateFMul(b_, c_, "") ad := b.CreateFMul(a_, d_, "") real_ := b.CreateFSub(ac, bd, "") imag_ := b.CreateFAdd(bc, ad, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.ADD: real_ := b.CreateFAdd(a_, c_, "") imag_ := b.CreateFAdd(b_, d_, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.SUB: real_ := b.CreateFSub(a_, c_, "") imag_ := b.CreateFSub(b_, d_, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.EQL: realeq := b.CreateFCmp(llvm.FloatOEQ, a_, c_, "") imageq := b.CreateFCmp(llvm.FloatOEQ, b_, d_, "") result = b.CreateAnd(realeq, imageq, "") default: panic(fmt.Errorf("unhandled operator: %v", op)) } return lhs.compiler.NewValue(result, lhs.typ) } // Floats and integers. // TODO determine the NaN rules. switch op { case token.MUL: if isFloat(lhs.typ) { result = b.CreateFMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.QUO: switch { case isFloat(lhs.typ): result = b.CreateFDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.REM: switch { case isFloat(lhs.typ): result = b.CreateFRem(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSRem(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateURem(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.ADD: if isFloat(lhs.typ) { result = b.CreateFAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SUB: if isFloat(lhs.typ) { result = b.CreateFSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SHL, token.SHR: return lhs.shift(rhs, op) case token.EQL: if isFloat(lhs.typ) { result = b.CreateFCmp(llvm.FloatOEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LSS: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GTR: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.AND: // a & b result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.AND_NOT: // a &^ b rhsval := rhs.LLVMValue() rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "") result = b.CreateAnd(lhs.LLVMValue(), rhsval, "") return lhs.compiler.NewValue(result, lhs.typ) case token.OR: // a | b result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.XOR: // a ^ b result = b.CreateXor(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } panic("unreachable") }
// phiValue returns a new value with the same value and type as the given Phi. // This is used for go.tools/ssa instructions that introduce new ssa.Values, // but would otherwise not generate a new LLVM value. func phiValue(c *compiler, v *LLVMValue) *LLVMValue { llv := v.LLVMValue() llv = c.builder.CreateSelect(llvm.ConstAllOnes(llvm.Int1Type()), llv, llv, "") return c.NewValue(llv, v.Type()) }
func constInt1(v bool) llvm.Value { if v { return llvm.ConstAllOnes(llvm.Int1Type()) } return llvm.ConstNull(llvm.Int1Type()) }
// convertI2I converts an interface to another interface. func (v *LLVMValue) convertI2I(iface *types.Interface) (result *LLVMValue, success *LLVMValue) { c := v.compiler builder := v.compiler.builder src_typ := v.Type().Underlying() val := v.LLVMValue() zero_iface_struct := llvm.ConstNull(c.types.ToLLVM(iface)) iface_struct := zero_iface_struct dynamicType := builder.CreateExtractValue(val, 0, "") receiver := builder.CreateExtractValue(val, 1, "") // TODO check whether the functions in the struct take // value or pointer receivers. // TODO handle dynamic interface conversion (non-subset). srciface := src_typ.(*types.Interface) for i := 0; i < iface.NumMethods(); i++ { m := iface.Method(i) // FIXME sort methods somewhere, make loop linear. var mi int for ; mi < srciface.NumMethods(); mi++ { if srciface.Method(i).Name() == m.Name() { break } } if mi >= srciface.NumMethods() { goto check_dynamic } else { fptr := builder.CreateExtractValue(val, mi+2, "") 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.NewValue(iface_struct, iface) success = c.NewValue(llvm.ConstAllOnes(llvm.Int1Type()), types.Typ[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] var vptr llvm.Value if v.pointer != nil { vptr = v.pointer.LLVMValue() } else { vptr = c.builder.CreateAlloca(val.Type(), "") c.builder.CreateStore(val, vptr) } 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.NewValue(value, iface) success = c.NewValue(ok, types.Typ[types.Bool]) return result, success }
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 (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 := struct_fields[0].Type.(types.Type) 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) logicalop := token.LAND if op == token.NEQ { logicalop = token.LOR } result := first for i := 1; i < element_types_count; i++ { result = c.compileLogicalOp(logicalop, result, func() Value { t := struct_fields[i].Type.(types.Type) lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) return lhs.BinaryOp(op, rhs) }) } return result } case *types.Interface: if rhsisnil { typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") valueNull := 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") } // Numbers. // 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.AND_NOT: // a &^ b rhsval := rhs.LLVMValue() rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "") result = b.CreateAnd(lhs.LLVMValue(), rhsval, "") 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") }
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 rhs := rhs_.(*LLVMValue) switch typ := lhs.typ.Underlying().(type) { case *types.Struct: // TODO(axw) use runtime equality algorithm (will be suitably inlined). // For now, we use compare all fields unconditionally and bitwise AND // to avoid branching (i.e. so we don't create additional blocks). var value Value = c.NewValue(boolLLVMValue(true), types.Typ[types.Bool]) for i := 0; i < typ.NumFields(); i++ { t := typ.Field(i).Type() lhs := c.NewValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) rhs := c.NewValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) value = value.BinaryOp(token.AND, lhs.BinaryOp(token.EQL, rhs)) } return value case *types.Slice: // []T == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) case *types.Signature: // func == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) case *types.Interface: return c.compareInterfaces(lhs, rhs) } // Strings. if isString(lhs.typ) { if isString(rhs.typ) { 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") } // Complex numbers. if isComplex(lhs.typ) { // XXX Should we represent complex numbers as vectors? lhsval := lhs.LLVMValue() rhsval := rhs.LLVMValue() a_ := b.CreateExtractValue(lhsval, 0, "") b_ := b.CreateExtractValue(lhsval, 1, "") c_ := b.CreateExtractValue(rhsval, 0, "") d_ := b.CreateExtractValue(rhsval, 1, "") switch op { case token.QUO: // (a+bi)/(c+di) = (ac+bd)/(c**2+d**2) + (bc-ad)/(c**2+d**2)i ac := b.CreateFMul(a_, c_, "") bd := b.CreateFMul(b_, d_, "") bc := b.CreateFMul(b_, c_, "") ad := b.CreateFMul(a_, d_, "") cpow2 := b.CreateFMul(c_, c_, "") dpow2 := b.CreateFMul(d_, d_, "") denom := b.CreateFAdd(cpow2, dpow2, "") realnumer := b.CreateFAdd(ac, bd, "") imagnumer := b.CreateFSub(bc, ad, "") real_ := b.CreateFDiv(realnumer, denom, "") imag_ := b.CreateFDiv(imagnumer, denom, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.MUL: // (a+bi)(c+di) = (ac-bd)+(bc+ad)i ac := b.CreateFMul(a_, c_, "") bd := b.CreateFMul(b_, d_, "") bc := b.CreateFMul(b_, c_, "") ad := b.CreateFMul(a_, d_, "") real_ := b.CreateFSub(ac, bd, "") imag_ := b.CreateFAdd(bc, ad, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.ADD: real_ := b.CreateFAdd(a_, c_, "") imag_ := b.CreateFAdd(b_, d_, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.SUB: real_ := b.CreateFSub(a_, c_, "") imag_ := b.CreateFSub(b_, d_, "") lhsval = b.CreateInsertValue(lhsval, real_, 0, "") result = b.CreateInsertValue(lhsval, imag_, 1, "") case token.EQL: realeq := b.CreateFCmp(llvm.FloatOEQ, a_, c_, "") imageq := b.CreateFCmp(llvm.FloatOEQ, b_, d_, "") result = b.CreateAnd(realeq, imageq, "") default: panic(fmt.Errorf("unhandled operator: %v", op)) } return lhs.compiler.NewValue(result, lhs.typ) } // Floats and integers. // TODO determine the NaN rules. switch op { case token.MUL: if isFloat(lhs.typ) { result = b.CreateFMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.QUO: switch { case isFloat(lhs.typ): result = b.CreateFDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.REM: switch { case isFloat(lhs.typ): result = b.CreateFRem(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSRem(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateURem(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.ADD: if isFloat(lhs.typ) { result = b.CreateFAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SUB: if isFloat(lhs.typ) { result = b.CreateFSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SHL, token.SHR: return lhs.shift(rhs, op) case token.EQL: if isFloat(lhs.typ) { result = b.CreateFCmp(llvm.FloatOEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LSS: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GTR: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.AND: // a & b result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.AND_NOT: // a &^ b rhsval := rhs.LLVMValue() rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "") result = b.CreateAnd(lhs.LLVMValue(), rhsval, "") return lhs.compiler.NewValue(result, lhs.typ) case token.OR: // a | b result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.XOR: // a ^ b result = b.CreateXor(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } 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 // We can avoid various runtime calls by treating // nil specially. For all nil-able types, nil is // equivalent to the zero value. rhs := rhs_.(*LLVMValue) rhsisnil := rhs.pointer == nil && rhs.LLVMValue().IsNull() switch typ := lhs.typ.Underlying().(type) { case *types.Struct: element_types_count := lhs.LLVMValue().Type().StructElementTypesCount() if element_types_count > 0 { t := typ.Field(0).Type first_lhs := c.NewValue(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), t) first_rhs := c.NewValue(b.CreateExtractValue(rhs.LLVMValue(), 0, ""), t) first := first_lhs.BinaryOp(op, first_rhs) logicalop := token.LAND if op == token.NEQ { logicalop = token.LOR } result := first for i := 1; i < element_types_count; i++ { result = c.compileLogicalOp(logicalop, result, func() Value { t := typ.Field(i).Type lhs := c.NewValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) rhs := c.NewValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) return lhs.BinaryOp(op, rhs) }) } return result } case *types.Interface: if rhsisnil { typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") valueNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result := b.CreateAnd(typeNull, valueNull, "") return c.NewValue(result, types.Typ[types.Bool]) } if _, ok := rhs.typ.Underlying().(*types.Interface); ok { return lhs.compareI2I(rhs) } return lhs.compareI2V(rhs) case *types.Slice: // []T == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) case *types.Signature: // func == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewValue(isnil, types.Typ[types.Bool]) } // Strings. if isString(lhs.typ) { if isString(rhs.typ) { 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") } // Numbers. // Determine whether to use integer or floating point instructions. // TODO determine the NaN rules. switch op { case token.MUL: if isFloat(lhs.typ) { result = b.CreateFMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.QUO: switch { case isFloat(lhs.typ): result = b.CreateFDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.REM: switch { case isFloat(lhs.typ): result = b.CreateFRem(lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateSRem(lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateURem(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.ADD: if isFloat(lhs.typ) { result = b.CreateFAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SUB: if isFloat(lhs.typ) { result = b.CreateFSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.SHL: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) result = b.CreateShl(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.SHR: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) if !isUnsigned(lhs.Type()) { result = b.CreateAShr(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateLShr(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, lhs.typ) case token.NEQ: if isFloat(lhs.typ) { result = b.CreateFCmp(llvm.FloatONE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntNE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.EQL: if isFloat(lhs.typ) { result = b.CreateFCmp(llvm.FloatOEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LSS: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.LEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOLE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSLE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GTR: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGT, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGT, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.GEQ: switch { case isFloat(lhs.typ): result = b.CreateFCmp(llvm.FloatOGE, lhs.LLVMValue(), rhs.LLVMValue(), "") case !isUnsigned(lhs.typ): result = b.CreateICmp(llvm.IntSGE, lhs.LLVMValue(), rhs.LLVMValue(), "") default: result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewValue(result, types.Typ[types.Bool]) case token.AND: // a & b result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.AND_NOT: // a &^ b rhsval := rhs.LLVMValue() rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "") result = b.CreateAnd(lhs.LLVMValue(), rhsval, "") return lhs.compiler.NewValue(result, lhs.typ) case token.OR: // a | b result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) case token.XOR: // a ^ b result = b.CreateXor(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewValue(result, lhs.typ) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } panic("unreachable") }