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.
Этот коммит содержится в:
Ayke van Laethem 2019-11-22 19:53:16 +01:00 коммит произвёл Ron Evans
родитель 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) 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. // 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 { func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
expanded := make([]llvm.Value, 0, len(args)) 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) 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 // Expand an argument type to a list that can be used in a function call
// parameter list. // parameter list.
func expandFormalParamType(t llvm.Type) []llvm.Type { 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 // 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. // with the passed in type if this is not possible.
func flattenAggregateType(t llvm.Type) []llvm.Type { 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. // Collapse a list of fields into its original value.
func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value { func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
param, remaining := c.collapseFormalParamInternal(t, fields) 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: case *ssa.BinOp:
x := c.getValue(frame, expr.X) x := c.getValue(frame, expr.X)
y := c.getValue(frame, expr.Y) 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: case *ssa.Call:
// Passing the current task here to the subroutine. It is only used when // Passing the current task here to the subroutine. It is only used when
// the subroutine is blocking. // 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) { switch typ := typ.Underlying().(type) {
case *types.Basic: case *types.Basic:
if typ.Info()&types.IsInteger != 0 { 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 signed := typ.Info()&types.IsUnsigned == 0
switch op { switch op {
case token.ADD: // + case token.ADD: // +
return c.builder.CreateAdd(x, y, ""), nil return b.CreateAdd(x, y, ""), nil
case token.SUB: // - case token.SUB: // -
return c.builder.CreateSub(x, y, ""), nil return b.CreateSub(x, y, ""), nil
case token.MUL: // * case token.MUL: // *
return c.builder.CreateMul(x, y, ""), nil return b.CreateMul(x, y, ""), nil
case token.QUO: // / case token.QUO: // /
if signed { if signed {
return c.builder.CreateSDiv(x, y, ""), nil return b.CreateSDiv(x, y, ""), nil
} else { } else {
return c.builder.CreateUDiv(x, y, ""), nil return b.CreateUDiv(x, y, ""), nil
} }
case token.REM: // % case token.REM: // %
if signed { if signed {
return c.builder.CreateSRem(x, y, ""), nil return b.CreateSRem(x, y, ""), nil
} else { } else {
return c.builder.CreateURem(x, y, ""), nil return b.CreateURem(x, y, ""), nil
} }
case token.AND: // & case token.AND: // &
return c.builder.CreateAnd(x, y, ""), nil return b.CreateAnd(x, y, ""), nil
case token.OR: // | case token.OR: // |
return c.builder.CreateOr(x, y, ""), nil return b.CreateOr(x, y, ""), nil
case token.XOR: // ^ case token.XOR: // ^
return c.builder.CreateXor(x, y, ""), nil return b.CreateXor(x, y, ""), nil
case token.SHL, token.SHR: case token.SHL, token.SHR:
sizeX := c.targetData.TypeAllocSize(x.Type()) sizeX := b.targetData.TypeAllocSize(x.Type())
sizeY := c.targetData.TypeAllocSize(y.Type()) sizeY := b.targetData.TypeAllocSize(y.Type())
if sizeX > sizeY { if sizeX > sizeY {
// x and y must have equal sizes, make Y bigger in this case. // 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 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 { } else if sizeX < sizeY {
// What about shifting more than the integer width? // What about shifting more than the integer width?
// I'm not entirely sure what the Go spec is on that, but as // I'm not entirely sure what the Go spec is on that, but as
// Intel CPUs have undefined behavior when shifting more // Intel CPUs have undefined behavior when shifting more
// than the integer width I'm assuming it is also undefined // than the integer width I'm assuming it is also undefined
// in Go. // in Go.
y = c.builder.CreateTrunc(y, x.Type(), "") y = b.CreateTrunc(y, x.Type(), "")
} }
switch op { switch op {
case token.SHL: // << case token.SHL: // <<
return c.builder.CreateShl(x, y, ""), nil return b.CreateShl(x, y, ""), nil
case token.SHR: // >> case token.SHR: // >>
if signed { if signed {
return c.builder.CreateAShr(x, y, ""), nil return b.CreateAShr(x, y, ""), nil
} else { } else {
return c.builder.CreateLShr(x, y, ""), nil return b.CreateLShr(x, y, ""), nil
} }
default: default:
panic("unreachable") panic("unreachable")
} }
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil return b.CreateICmp(llvm.IntNE, x, y, ""), nil
case token.AND_NOT: // &^ case token.AND_NOT: // &^
// Go specific. Calculate "and not" with x & (~y) // Go specific. Calculate "and not" with x & (~y)
inv := c.builder.CreateNot(y, "") // ~y inv := b.CreateNot(y, "") // ~y
return c.builder.CreateAnd(x, inv, ""), nil return b.CreateAnd(x, inv, ""), nil
case token.LSS: // < case token.LSS: // <
if signed { if signed {
return c.builder.CreateICmp(llvm.IntSLT, x, y, ""), nil return b.CreateICmp(llvm.IntSLT, x, y, ""), nil
} else { } else {
return c.builder.CreateICmp(llvm.IntULT, x, y, ""), nil return b.CreateICmp(llvm.IntULT, x, y, ""), nil
} }
case token.LEQ: // <= case token.LEQ: // <=
if signed { if signed {
return c.builder.CreateICmp(llvm.IntSLE, x, y, ""), nil return b.CreateICmp(llvm.IntSLE, x, y, ""), nil
} else { } else {
return c.builder.CreateICmp(llvm.IntULE, x, y, ""), nil return b.CreateICmp(llvm.IntULE, x, y, ""), nil
} }
case token.GTR: // > case token.GTR: // >
if signed { if signed {
return c.builder.CreateICmp(llvm.IntSGT, x, y, ""), nil return b.CreateICmp(llvm.IntSGT, x, y, ""), nil
} else { } else {
return c.builder.CreateICmp(llvm.IntUGT, x, y, ""), nil return b.CreateICmp(llvm.IntUGT, x, y, ""), nil
} }
case token.GEQ: // >= case token.GEQ: // >=
if signed { if signed {
return c.builder.CreateICmp(llvm.IntSGE, x, y, ""), nil return b.CreateICmp(llvm.IntSGE, x, y, ""), nil
} else { } else {
return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil return b.CreateICmp(llvm.IntUGE, x, y, ""), nil
} }
default: default:
panic("binop on integer: " + op.String()) 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 // Operations on floats
switch op { switch op {
case token.ADD: // + case token.ADD: // +
return c.builder.CreateFAdd(x, y, ""), nil return b.CreateFAdd(x, y, ""), nil
case token.SUB: // - case token.SUB: // -
return c.builder.CreateFSub(x, y, ""), nil return b.CreateFSub(x, y, ""), nil
case token.MUL: // * case token.MUL: // *
return c.builder.CreateFMul(x, y, ""), nil return b.CreateFMul(x, y, ""), nil
case token.QUO: // / case token.QUO: // /
return c.builder.CreateFDiv(x, y, ""), nil return b.CreateFDiv(x, y, ""), nil
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil return b.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateFCmp(llvm.FloatUNE, x, y, ""), nil return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
case token.LSS: // < case token.LSS: // <
return c.builder.CreateFCmp(llvm.FloatULT, x, y, ""), nil return b.CreateFCmp(llvm.FloatULT, x, y, ""), nil
case token.LEQ: // <= case token.LEQ: // <=
return c.builder.CreateFCmp(llvm.FloatULE, x, y, ""), nil return b.CreateFCmp(llvm.FloatULE, x, y, ""), nil
case token.GTR: // > case token.GTR: // >
return c.builder.CreateFCmp(llvm.FloatUGT, x, y, ""), nil return b.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
case token.GEQ: // >= case token.GEQ: // >=
return c.builder.CreateFCmp(llvm.FloatUGE, x, y, ""), nil return b.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
default: default:
panic("binop on float: " + op.String()) panic("binop on float: " + op.String())
} }
} else if typ.Info()&types.IsComplex != 0 { } else if typ.Info()&types.IsComplex != 0 {
r1 := c.builder.CreateExtractValue(x, 0, "r1") r1 := b.CreateExtractValue(x, 0, "r1")
r2 := c.builder.CreateExtractValue(y, 0, "r2") r2 := b.CreateExtractValue(y, 0, "r2")
i1 := c.builder.CreateExtractValue(x, 1, "i1") i1 := b.CreateExtractValue(x, 1, "i1")
i2 := c.builder.CreateExtractValue(y, 1, "i2") i2 := b.CreateExtractValue(y, 1, "i2")
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "") req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "") ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
return c.builder.CreateAnd(req, ieq, ""), nil return b.CreateAnd(req, ieq, ""), nil
case token.NEQ: // != case token.NEQ: // !=
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "") req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "") ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
neq := c.builder.CreateAnd(req, ieq, "") neq := b.CreateAnd(req, ieq, "")
return c.builder.CreateNot(neq, ""), nil return b.CreateNot(neq, ""), nil
case token.ADD, token.SUB: case token.ADD, token.SUB:
var r, i llvm.Value var r, i llvm.Value
switch op { switch op {
case token.ADD: case token.ADD:
r = c.builder.CreateFAdd(r1, r2, "") r = b.CreateFAdd(r1, r2, "")
i = c.builder.CreateFAdd(i1, i2, "") i = b.CreateFAdd(i1, i2, "")
case token.SUB: case token.SUB:
r = c.builder.CreateFSub(r1, r2, "") r = b.CreateFSub(r1, r2, "")
i = c.builder.CreateFSub(i1, i2, "") i = b.CreateFSub(i1, i2, "")
default: default:
panic("unreachable") panic("unreachable")
} }
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "") cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = c.builder.CreateInsertValue(cplx, i, 1, "") cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil return cplx, nil
case token.MUL: case token.MUL:
// Complex multiplication follows the current implementation in // 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 // 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 // See https://github.com/golang/go/issues/29846 for a related
// discussion. // discussion.
r := c.builder.CreateFSub(c.builder.CreateFMul(r1, r2, ""), c.builder.CreateFMul(i1, i2, ""), "") r := b.CreateFSub(b.CreateFMul(r1, r2, ""), b.CreateFMul(i1, i2, ""), "")
i := c.builder.CreateFAdd(c.builder.CreateFMul(r1, i2, ""), c.builder.CreateFMul(i1, r2, ""), "") i := b.CreateFAdd(b.CreateFMul(r1, i2, ""), b.CreateFMul(i1, r2, ""), "")
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "") cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = c.builder.CreateInsertValue(cplx, i, 1, "") cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil return cplx, nil
case token.QUO: case token.QUO:
// Complex division. // Complex division.
@ -2080,9 +2086,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
// inline. // inline.
switch r1.Type().TypeKind() { switch r1.Type().TypeKind() {
case llvm.FloatTypeKind: case llvm.FloatTypeKind:
return c.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil return b.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil
case llvm.DoubleTypeKind: case llvm.DoubleTypeKind:
return c.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil return b.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil
default: default:
panic("unexpected complex type") 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 // Operations on booleans
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil return b.CreateICmp(llvm.IntNE, x, y, ""), nil
default: default:
panic("binop on bool: " + op.String()) 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 // Operations on pointers
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil return b.CreateICmp(llvm.IntNE, x, y, ""), nil
default: default:
panic("binop on pointer: " + op.String()) 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 // Operations on strings
switch op { switch op {
case token.ADD: // + case token.ADD: // +
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil return b.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
case token.EQL: // == case token.EQL: // ==
return c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil return b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil
case token.NEQ: // != case token.NEQ: // !=
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "") result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
return c.builder.CreateNot(result, ""), nil return b.CreateNot(result, ""), nil
case token.LSS: // < case token.LSS: // <
return c.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
case token.LEQ: // <= case token.LEQ: // <=
result := c.createRuntimeCall("stringLess", []llvm.Value{y, x}, "") result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
return c.builder.CreateNot(result, ""), nil return b.CreateNot(result, ""), nil
case token.GTR: // > case token.GTR: // >
result := c.createRuntimeCall("stringLess", []llvm.Value{x, y}, "") result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
return c.builder.CreateNot(result, ""), nil return b.CreateNot(result, ""), nil
case token.GEQ: // >= case token.GEQ: // >=
return c.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
default: default:
panic("binop on string: " + op.String()) panic("binop on string: " + op.String())
} }
} else { } 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: case *types.Signature:
// Get raw scalars from the function value and compare those. // 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. // have some way of getting a scalar value identifying the function.
// This is safe: function pointers are generally not comparable // This is safe: function pointers are generally not comparable
// against each other, only against nil. So one of these has to be nil. // against each other, only against nil. So one of these has to be nil.
x = c.extractFuncScalar(x) x = b.extractFuncScalar(x)
y = c.extractFuncScalar(y) y = b.extractFuncScalar(y)
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil return b.CreateICmp(llvm.IntNE, x, y, ""), nil
default: 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: case *types.Interface:
switch op { switch op {
case token.EQL, token.NEQ: // ==, != 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 { if op == token.NEQ {
result = c.builder.CreateNot(result, "") result = b.CreateNot(result, "")
} }
return result, nil return result, nil
default: 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: case *types.Chan, *types.Map, *types.Pointer:
// Maps are in general not comparable, but can be compared against nil // 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. // are created with the same call to make or if both are nil.
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil return b.CreateICmp(llvm.IntNE, x, y, ""), nil
default: 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: case *types.Slice:
// Slices are in general not comparable, but can be compared against // 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. // nil. Assume at least one of them is nil to make the code easier.
xPtr := c.builder.CreateExtractValue(x, 0, "") xPtr := b.CreateExtractValue(x, 0, "")
yPtr := c.builder.CreateExtractValue(y, 0, "") yPtr := b.CreateExtractValue(y, 0, "")
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil return b.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil return b.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
default: 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: case *types.Array:
// Compare each array element and combine the result. From the spec: // Compare each array element and combine the result. From the spec:
// Array values are comparable if values of the array element type // Array values are comparable if values of the array element type
// are comparable. Two array values are equal if their corresponding // are comparable. Two array values are equal if their corresponding
// elements are equal. // 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++ { for i := 0; i < int(typ.Len()); i++ {
xField := c.builder.CreateExtractValue(x, i, "") xField := b.CreateExtractValue(x, i, "")
yField := c.builder.CreateExtractValue(y, i, "") yField := b.CreateExtractValue(y, i, "")
fieldEqual, err := c.parseBinOp(token.EQL, typ.Elem(), xField, yField, pos) fieldEqual, err := b.createBinOp(token.EQL, typ.Elem(), xField, yField, pos)
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
result = c.builder.CreateAnd(result, fieldEqual, "") result = b.CreateAnd(result, fieldEqual, "")
} }
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return result, nil return result, nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateNot(result, ""), nil return b.CreateNot(result, ""), nil
default: 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: case *types.Struct:
// Compare each struct field and combine the result. From the spec: // Compare each struct field and combine the result. From the spec:
// Struct values are comparable if all their fields are comparable. // Struct values are comparable if all their fields are comparable.
// Two struct values are equal if their corresponding non-blank // Two struct values are equal if their corresponding non-blank
// fields are equal. // 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++ { for i := 0; i < typ.NumFields(); i++ {
if typ.Field(i).Name() == "_" { if typ.Field(i).Name() == "_" {
// skip blank fields // skip blank fields
continue continue
} }
fieldType := typ.Field(i).Type() fieldType := typ.Field(i).Type()
xField := c.builder.CreateExtractValue(x, i, "") xField := b.CreateExtractValue(x, i, "")
yField := c.builder.CreateExtractValue(y, i, "") yField := b.CreateExtractValue(y, i, "")
fieldEqual, err := c.parseBinOp(token.EQL, fieldType, xField, yField, pos) fieldEqual, err := b.createBinOp(token.EQL, fieldType, xField, yField, pos)
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
result = c.builder.CreateAnd(result, fieldEqual, "") result = b.CreateAnd(result, fieldEqual, "")
} }
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return result, nil return result, nil
case token.NEQ: // != case token.NEQ: // !=
return c.builder.CreateNot(result, ""), nil return b.CreateNot(result, ""), nil
default: 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: 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, "") 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 // extractFuncContext extracts the context pointer from this function value. It
// is a cheap operation. // is a cheap operation.
func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value { func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {