compiler: refactor createBinOp
This commit unfortunately introduces a significant amount of code duplication. However, all that duplicate code should be removed once this refactor is done.
Этот коммит содержится в:
родитель
b5e29bf0c1
коммит
840acdd316
3 изменённых файлов: 188 добавлений и 112 удалений
|
@ -31,6 +31,18 @@ func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name stri
|
|||
return c.createCall(fn.LLVMFn, args, name)
|
||||
}
|
||||
|
||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
fullName := "runtime." + fnName
|
||||
fn := b.mod.NamedFunction(fullName)
|
||||
if fn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fullName)
|
||||
}
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
|
||||
return b.createCall(fn, args, name)
|
||||
}
|
||||
|
||||
// Create a call to the given function with the arguments possibly expanded.
|
||||
func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
expanded := make([]llvm.Value, 0, len(args))
|
||||
|
@ -41,6 +53,17 @@ func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llv
|
|||
return c.builder.CreateCall(fn, expanded, name)
|
||||
}
|
||||
|
||||
// createCall creates a call to the given function with the arguments possibly
|
||||
// expanded.
|
||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
expanded := make([]llvm.Value, 0, len(args))
|
||||
for _, arg := range args {
|
||||
fragments := b.expandFormalParam(arg)
|
||||
expanded = append(expanded, fragments...)
|
||||
}
|
||||
return b.CreateCall(fn, expanded, name)
|
||||
}
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
func expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||
|
@ -99,6 +122,30 @@ func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
|||
}
|
||||
}
|
||||
|
||||
// expandFormalParam splits a formal param value into pieces, so it can be
|
||||
// passed directly as part of a function call. For example, it splits up small
|
||||
// structs into individual fields. It is the equivalent of expandFormalParamType
|
||||
// for parameter values.
|
||||
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
switch v.Type().TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fieldTypes := flattenAggregateType(v.Type())
|
||||
if len(fieldTypes) <= MaxFieldsPerParam {
|
||||
fields := b.flattenAggregate(v)
|
||||
if len(fields) != len(fieldTypes) {
|
||||
panic("type and value param lowering don't match")
|
||||
}
|
||||
return fields
|
||||
} else {
|
||||
// failed to lower
|
||||
return []llvm.Value{v}
|
||||
}
|
||||
default:
|
||||
// TODO: split small arrays
|
||||
return []llvm.Value{v}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||
// with the passed in type if this is not possible.
|
||||
func flattenAggregateType(t llvm.Type) []llvm.Type {
|
||||
|
@ -153,6 +200,23 @@ func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
|||
}
|
||||
}
|
||||
|
||||
// flattenAggregate breaks down a struct into its elementary values for argument
|
||||
// passing. It is the value equivalent of flattenAggregateType
|
||||
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||
switch v.Type().TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount())
|
||||
for i := range v.Type().StructElementTypes() {
|
||||
subfield := b.CreateExtractValue(v, i, "")
|
||||
subfields := b.flattenAggregate(subfield)
|
||||
fields = append(fields, subfields...)
|
||||
}
|
||||
return fields
|
||||
default:
|
||||
return []llvm.Value{v}
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse a list of fields into its original value.
|
||||
func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
|
||||
param, remaining := c.collapseFormalParamInternal(t, fields)
|
||||
|
|
|
@ -1460,7 +1460,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
case *ssa.BinOp:
|
||||
x := c.getValue(frame, expr.X)
|
||||
y := c.getValue(frame, expr.Y)
|
||||
return c.parseBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
|
||||
return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
|
||||
case *ssa.Call:
|
||||
// Passing the current task here to the subroutine. It is only used when
|
||||
// the subroutine is blocking.
|
||||
|
@ -1905,7 +1905,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) {
|
||||
// createBinOp creates a LLVM binary operation (add, sub, mul, etc) for a Go
|
||||
// binary operation. This is almost a direct mapping, but there are some subtle
|
||||
// differences such as the requirement in LLVM IR that both sides must have the
|
||||
// same type, even for bitshifts. Also, signedness in Go is encoded in the type
|
||||
// and is encoded in the operation in LLVM IR: this is important for some
|
||||
// operations such as divide.
|
||||
func (b *builder) createBinOp(op token.Token, typ types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) {
|
||||
switch typ := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if typ.Info()&types.IsInteger != 0 {
|
||||
|
@ -1913,87 +1919,87 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
signed := typ.Info()&types.IsUnsigned == 0
|
||||
switch op {
|
||||
case token.ADD: // +
|
||||
return c.builder.CreateAdd(x, y, ""), nil
|
||||
return b.CreateAdd(x, y, ""), nil
|
||||
case token.SUB: // -
|
||||
return c.builder.CreateSub(x, y, ""), nil
|
||||
return b.CreateSub(x, y, ""), nil
|
||||
case token.MUL: // *
|
||||
return c.builder.CreateMul(x, y, ""), nil
|
||||
return b.CreateMul(x, y, ""), nil
|
||||
case token.QUO: // /
|
||||
if signed {
|
||||
return c.builder.CreateSDiv(x, y, ""), nil
|
||||
return b.CreateSDiv(x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateUDiv(x, y, ""), nil
|
||||
return b.CreateUDiv(x, y, ""), nil
|
||||
}
|
||||
case token.REM: // %
|
||||
if signed {
|
||||
return c.builder.CreateSRem(x, y, ""), nil
|
||||
return b.CreateSRem(x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateURem(x, y, ""), nil
|
||||
return b.CreateURem(x, y, ""), nil
|
||||
}
|
||||
case token.AND: // &
|
||||
return c.builder.CreateAnd(x, y, ""), nil
|
||||
return b.CreateAnd(x, y, ""), nil
|
||||
case token.OR: // |
|
||||
return c.builder.CreateOr(x, y, ""), nil
|
||||
return b.CreateOr(x, y, ""), nil
|
||||
case token.XOR: // ^
|
||||
return c.builder.CreateXor(x, y, ""), nil
|
||||
return b.CreateXor(x, y, ""), nil
|
||||
case token.SHL, token.SHR:
|
||||
sizeX := c.targetData.TypeAllocSize(x.Type())
|
||||
sizeY := c.targetData.TypeAllocSize(y.Type())
|
||||
sizeX := b.targetData.TypeAllocSize(x.Type())
|
||||
sizeY := b.targetData.TypeAllocSize(y.Type())
|
||||
if sizeX > sizeY {
|
||||
// x and y must have equal sizes, make Y bigger in this case.
|
||||
// y is unsigned, this has been checked by the Go type checker.
|
||||
y = c.builder.CreateZExt(y, x.Type(), "")
|
||||
y = b.CreateZExt(y, x.Type(), "")
|
||||
} else if sizeX < sizeY {
|
||||
// What about shifting more than the integer width?
|
||||
// I'm not entirely sure what the Go spec is on that, but as
|
||||
// Intel CPUs have undefined behavior when shifting more
|
||||
// than the integer width I'm assuming it is also undefined
|
||||
// in Go.
|
||||
y = c.builder.CreateTrunc(y, x.Type(), "")
|
||||
y = b.CreateTrunc(y, x.Type(), "")
|
||||
}
|
||||
switch op {
|
||||
case token.SHL: // <<
|
||||
return c.builder.CreateShl(x, y, ""), nil
|
||||
return b.CreateShl(x, y, ""), nil
|
||||
case token.SHR: // >>
|
||||
if signed {
|
||||
return c.builder.CreateAShr(x, y, ""), nil
|
||||
return b.CreateAShr(x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateLShr(x, y, ""), nil
|
||||
return b.CreateLShr(x, y, ""), nil
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
case token.AND_NOT: // &^
|
||||
// Go specific. Calculate "and not" with x & (~y)
|
||||
inv := c.builder.CreateNot(y, "") // ~y
|
||||
return c.builder.CreateAnd(x, inv, ""), nil
|
||||
inv := b.CreateNot(y, "") // ~y
|
||||
return b.CreateAnd(x, inv, ""), nil
|
||||
case token.LSS: // <
|
||||
if signed {
|
||||
return c.builder.CreateICmp(llvm.IntSLT, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntSLT, x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateICmp(llvm.IntULT, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntULT, x, y, ""), nil
|
||||
}
|
||||
case token.LEQ: // <=
|
||||
if signed {
|
||||
return c.builder.CreateICmp(llvm.IntSLE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntSLE, x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateICmp(llvm.IntULE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntULE, x, y, ""), nil
|
||||
}
|
||||
case token.GTR: // >
|
||||
if signed {
|
||||
return c.builder.CreateICmp(llvm.IntSGT, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntSGT, x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateICmp(llvm.IntUGT, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntUGT, x, y, ""), nil
|
||||
}
|
||||
case token.GEQ: // >=
|
||||
if signed {
|
||||
return c.builder.CreateICmp(llvm.IntSGE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntSGE, x, y, ""), nil
|
||||
} else {
|
||||
return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntUGE, x, y, ""), nil
|
||||
}
|
||||
default:
|
||||
panic("binop on integer: " + op.String())
|
||||
|
@ -2002,58 +2008,58 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// Operations on floats
|
||||
switch op {
|
||||
case token.ADD: // +
|
||||
return c.builder.CreateFAdd(x, y, ""), nil
|
||||
return b.CreateFAdd(x, y, ""), nil
|
||||
case token.SUB: // -
|
||||
return c.builder.CreateFSub(x, y, ""), nil
|
||||
return b.CreateFSub(x, y, ""), nil
|
||||
case token.MUL: // *
|
||||
return c.builder.CreateFMul(x, y, ""), nil
|
||||
return b.CreateFMul(x, y, ""), nil
|
||||
case token.QUO: // /
|
||||
return c.builder.CreateFDiv(x, y, ""), nil
|
||||
return b.CreateFDiv(x, y, ""), nil
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
|
||||
case token.LSS: // <
|
||||
return c.builder.CreateFCmp(llvm.FloatULT, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatULT, x, y, ""), nil
|
||||
case token.LEQ: // <=
|
||||
return c.builder.CreateFCmp(llvm.FloatULE, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatULE, x, y, ""), nil
|
||||
case token.GTR: // >
|
||||
return c.builder.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
|
||||
case token.GEQ: // >=
|
||||
return c.builder.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
|
||||
default:
|
||||
panic("binop on float: " + op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
r1 := c.builder.CreateExtractValue(x, 0, "r1")
|
||||
r2 := c.builder.CreateExtractValue(y, 0, "r2")
|
||||
i1 := c.builder.CreateExtractValue(x, 1, "i1")
|
||||
i2 := c.builder.CreateExtractValue(y, 1, "i2")
|
||||
r1 := b.CreateExtractValue(x, 0, "r1")
|
||||
r2 := b.CreateExtractValue(y, 0, "r2")
|
||||
i1 := b.CreateExtractValue(x, 1, "i1")
|
||||
i2 := b.CreateExtractValue(y, 1, "i2")
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
return c.builder.CreateAnd(req, ieq, ""), nil
|
||||
req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
return b.CreateAnd(req, ieq, ""), nil
|
||||
case token.NEQ: // !=
|
||||
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
neq := c.builder.CreateAnd(req, ieq, "")
|
||||
return c.builder.CreateNot(neq, ""), nil
|
||||
req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
neq := b.CreateAnd(req, ieq, "")
|
||||
return b.CreateNot(neq, ""), nil
|
||||
case token.ADD, token.SUB:
|
||||
var r, i llvm.Value
|
||||
switch op {
|
||||
case token.ADD:
|
||||
r = c.builder.CreateFAdd(r1, r2, "")
|
||||
i = c.builder.CreateFAdd(i1, i2, "")
|
||||
r = b.CreateFAdd(r1, r2, "")
|
||||
i = b.CreateFAdd(i1, i2, "")
|
||||
case token.SUB:
|
||||
r = c.builder.CreateFSub(r1, r2, "")
|
||||
i = c.builder.CreateFSub(i1, i2, "")
|
||||
r = b.CreateFSub(r1, r2, "")
|
||||
i = b.CreateFSub(i1, i2, "")
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case token.MUL:
|
||||
// Complex multiplication follows the current implementation in
|
||||
|
@ -2068,11 +2074,11 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf#page=549
|
||||
// See https://github.com/golang/go/issues/29846 for a related
|
||||
// discussion.
|
||||
r := c.builder.CreateFSub(c.builder.CreateFMul(r1, r2, ""), c.builder.CreateFMul(i1, i2, ""), "")
|
||||
i := c.builder.CreateFAdd(c.builder.CreateFMul(r1, i2, ""), c.builder.CreateFMul(i1, r2, ""), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
r := b.CreateFSub(b.CreateFMul(r1, r2, ""), b.CreateFMul(i1, i2, ""), "")
|
||||
i := b.CreateFAdd(b.CreateFMul(r1, i2, ""), b.CreateFMul(i1, r2, ""), "")
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case token.QUO:
|
||||
// Complex division.
|
||||
|
@ -2080,9 +2086,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// inline.
|
||||
switch r1.Type().TypeKind() {
|
||||
case llvm.FloatTypeKind:
|
||||
return c.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil
|
||||
return b.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil
|
||||
case llvm.DoubleTypeKind:
|
||||
return c.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil
|
||||
return b.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil
|
||||
default:
|
||||
panic("unexpected complex type")
|
||||
}
|
||||
|
@ -2093,9 +2099,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// Operations on booleans
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
panic("binop on bool: " + op.String())
|
||||
}
|
||||
|
@ -2103,9 +2109,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// Operations on pointers
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
panic("binop on pointer: " + op.String())
|
||||
}
|
||||
|
@ -2113,27 +2119,27 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// Operations on strings
|
||||
switch op {
|
||||
case token.ADD: // +
|
||||
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
|
||||
return b.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
|
||||
case token.EQL: // ==
|
||||
return c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil
|
||||
return b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil
|
||||
case token.NEQ: // !=
|
||||
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
|
||||
return c.builder.CreateNot(result, ""), nil
|
||||
result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
|
||||
return b.CreateNot(result, ""), nil
|
||||
case token.LSS: // <
|
||||
return c.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
|
||||
return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
|
||||
case token.LEQ: // <=
|
||||
result := c.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
|
||||
return c.builder.CreateNot(result, ""), nil
|
||||
result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
|
||||
return b.CreateNot(result, ""), nil
|
||||
case token.GTR: // >
|
||||
result := c.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
|
||||
return c.builder.CreateNot(result, ""), nil
|
||||
result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
|
||||
return b.CreateNot(result, ""), nil
|
||||
case token.GEQ: // >=
|
||||
return c.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
|
||||
return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
|
||||
default:
|
||||
panic("binop on string: " + op.String())
|
||||
}
|
||||
} else {
|
||||
return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String())
|
||||
return llvm.Value{}, b.makeError(pos, "todo: unknown basic type in binop: "+typ.String())
|
||||
}
|
||||
case *types.Signature:
|
||||
// Get raw scalars from the function value and compare those.
|
||||
|
@ -2141,26 +2147,26 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// have some way of getting a scalar value identifying the function.
|
||||
// This is safe: function pointers are generally not comparable
|
||||
// against each other, only against nil. So one of these has to be nil.
|
||||
x = c.extractFuncScalar(x)
|
||||
y = c.extractFuncScalar(y)
|
||||
x = b.extractFuncScalar(x)
|
||||
y = b.extractFuncScalar(y)
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "binop on signature: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "binop on signature: "+op.String())
|
||||
}
|
||||
case *types.Interface:
|
||||
switch op {
|
||||
case token.EQL, token.NEQ: // ==, !=
|
||||
result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
|
||||
result := b.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
|
||||
if op == token.NEQ {
|
||||
result = c.builder.CreateNot(result, "")
|
||||
result = b.CreateNot(result, "")
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "binop on interface: "+op.String())
|
||||
}
|
||||
case *types.Chan, *types.Map, *types.Pointer:
|
||||
// Maps are in general not comparable, but can be compared against nil
|
||||
|
@ -2170,78 +2176,78 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||
// are created with the same call to make or if both are nil.
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on pointer: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "todo: binop on pointer: "+op.String())
|
||||
}
|
||||
case *types.Slice:
|
||||
// Slices are in general not comparable, but can be compared against
|
||||
// nil. Assume at least one of them is nil to make the code easier.
|
||||
xPtr := c.builder.CreateExtractValue(x, 0, "")
|
||||
yPtr := c.builder.CreateExtractValue(y, 0, "")
|
||||
xPtr := b.CreateExtractValue(x, 0, "")
|
||||
yPtr := b.CreateExtractValue(y, 0, "")
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
|
||||
return b.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
|
||||
return b.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on slice: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "todo: binop on slice: "+op.String())
|
||||
}
|
||||
case *types.Array:
|
||||
// Compare each array element and combine the result. From the spec:
|
||||
// Array values are comparable if values of the array element type
|
||||
// are comparable. Two array values are equal if their corresponding
|
||||
// elements are equal.
|
||||
result := llvm.ConstInt(c.ctx.Int1Type(), 1, true)
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, true)
|
||||
for i := 0; i < int(typ.Len()); i++ {
|
||||
xField := c.builder.CreateExtractValue(x, i, "")
|
||||
yField := c.builder.CreateExtractValue(y, i, "")
|
||||
fieldEqual, err := c.parseBinOp(token.EQL, typ.Elem(), xField, yField, pos)
|
||||
xField := b.CreateExtractValue(x, i, "")
|
||||
yField := b.CreateExtractValue(y, i, "")
|
||||
fieldEqual, err := b.createBinOp(token.EQL, typ.Elem(), xField, yField, pos)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
result = c.builder.CreateAnd(result, fieldEqual, "")
|
||||
result = b.CreateAnd(result, fieldEqual, "")
|
||||
}
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return result, nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateNot(result, ""), nil
|
||||
return b.CreateNot(result, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
case *types.Struct:
|
||||
// Compare each struct field and combine the result. From the spec:
|
||||
// Struct values are comparable if all their fields are comparable.
|
||||
// Two struct values are equal if their corresponding non-blank
|
||||
// fields are equal.
|
||||
result := llvm.ConstInt(c.ctx.Int1Type(), 1, true)
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, true)
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
if typ.Field(i).Name() == "_" {
|
||||
// skip blank fields
|
||||
continue
|
||||
}
|
||||
fieldType := typ.Field(i).Type()
|
||||
xField := c.builder.CreateExtractValue(x, i, "")
|
||||
yField := c.builder.CreateExtractValue(y, i, "")
|
||||
fieldEqual, err := c.parseBinOp(token.EQL, fieldType, xField, yField, pos)
|
||||
xField := b.CreateExtractValue(x, i, "")
|
||||
yField := b.CreateExtractValue(y, i, "")
|
||||
fieldEqual, err := b.createBinOp(token.EQL, fieldType, xField, yField, pos)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
result = c.builder.CreateAnd(result, fieldEqual, "")
|
||||
result = b.CreateAnd(result, fieldEqual, "")
|
||||
}
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return result, nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateNot(result, ""), nil
|
||||
return b.CreateNot(result, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop type: "+typ.String())
|
||||
return llvm.Value{}, b.makeError(pos, "todo: binop type: "+typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,12 @@ func (c *Compiler) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
|||
return c.builder.CreateExtractValue(funcValue, 1, "")
|
||||
}
|
||||
|
||||
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
||||
// a cheap operation.
|
||||
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||
return b.CreateExtractValue(funcValue, 1, "")
|
||||
}
|
||||
|
||||
// extractFuncContext extracts the context pointer from this function value. It
|
||||
// is a cheap operation.
|
||||
func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче