compiler: implement comparing structs directly

Этот коммит содержится в:
Ayke van Laethem 2018-10-23 13:26:01 +02:00
родитель cbd7d401fe
коммит e2f6aedd9d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
3 изменённых файлов: 121 добавлений и 33 удалений

Просмотреть файл

@ -2269,7 +2269,15 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
return buf, nil
case *ssa.BinOp:
return c.parseBinOp(frame, expr)
x, err := c.parseExpr(frame, expr.X)
if err != nil {
return llvm.Value{}, err
}
y, err := c.parseExpr(frame, expr.Y)
if err != nil {
return llvm.Value{}, err
}
return c.parseBinOp(expr.Op, expr.X.Type().Underlying(), x, y)
case *ssa.Call:
// Passing the current task here to the subroutine. It is only used when
// the subroutine is blocking.
@ -2834,21 +2842,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
}
func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error) {
x, err := c.parseExpr(frame, binop.X)
if err != nil {
return llvm.Value{}, err
}
y, err := c.parseExpr(frame, binop.Y)
if err != nil {
return llvm.Value{}, err
}
switch typ := binop.X.Type().Underlying().(type) {
func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) (llvm.Value, error) {
switch typ := typ.(type) {
case *types.Basic:
if typ.Info()&types.IsInteger != 0 {
// Operations on integers
signed := typ.Info()&types.IsUnsigned == 0
switch binop.Op {
switch op {
case token.ADD: // +
return c.builder.CreateAdd(x, y, ""), nil
case token.SUB: // -
@ -2888,7 +2888,7 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
// in Go.
y = c.builder.CreateTrunc(y, x.Type(), "")
}
switch binop.Op {
switch op {
case token.SHL: // <<
return c.builder.CreateShl(x, y, ""), nil
case token.SHR: // >>
@ -2933,11 +2933,11 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil
}
default:
return llvm.Value{}, errors.New("todo: binop on integer: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on integer: " + op.String())
}
} else if typ.Info()&types.IsFloat != 0 {
// Operations on floats
switch binop.Op {
switch op {
case token.ADD:
return c.builder.CreateFAdd(x, y, ""), nil
case token.SUB: // -
@ -2961,41 +2961,41 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
case token.GEQ: // >=
return c.builder.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
default:
return llvm.Value{}, errors.New("todo: binop on float: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on float: " + op.String())
}
} else if typ.Info()&types.IsBoolean != 0 {
// Operations on booleans
switch binop.Op {
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
default:
return llvm.Value{}, errors.New("todo: binop on boolean: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on boolean: " + op.String())
}
} else if typ.Kind() == types.UnsafePointer {
// Operations on pointers
switch binop.Op {
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
default:
return llvm.Value{}, errors.New("todo: binop on pointer: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on pointer: " + op.String())
}
} else if typ.Info()&types.IsString != 0 {
// Operations on strings
switch binop.Op {
switch op {
case token.ADD:
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
case token.EQL, token.NEQ: // ==, !=
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
if binop.Op == token.NEQ {
if op == token.NEQ {
result = c.builder.CreateNot(result, "")
}
return result, nil
default:
return llvm.Value{}, errors.New("todo: binop on string: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on string: " + op.String())
}
} else {
return llvm.Value{}, errors.New("todo: unknown basic type in binop: " + typ.String())
@ -3010,49 +3010,78 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
x = c.builder.CreateExtractValue(x, 1, "")
y = c.builder.CreateExtractValue(y, 1, "")
}
switch binop.Op {
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
default:
return llvm.Value{}, errors.New("binop on signature: " + binop.Op.String())
return llvm.Value{}, errors.New("binop on signature: " + op.String())
}
case *types.Interface:
switch binop.Op {
switch op {
case token.EQL, token.NEQ: // ==, !=
result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
if binop.Op == token.NEQ {
if op == token.NEQ {
result = c.builder.CreateNot(result, "")
}
return result, nil
default:
return llvm.Value{}, errors.New("binop on interface: " + binop.Op.String())
return llvm.Value{}, errors.New("binop on interface: " + op.String())
}
case *types.Pointer:
switch binop.Op {
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
default:
return llvm.Value{}, errors.New("todo: binop on pointer: " + binop.Op.String())
return llvm.Value{}, errors.New("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, "")
switch binop.Op {
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
case token.NEQ: // !=
return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
default:
return llvm.Value{}, errors.New("todo: binop on slice: " + binop.Op.String())
return llvm.Value{}, errors.New("todo: binop on slice: " + 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)
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)
if err != nil {
return llvm.Value{}, err
}
result = c.builder.CreateAnd(result, fieldEqual, "")
}
switch op {
case token.EQL: // ==
return result, nil
case token.NEQ: // !=
return c.builder.CreateNot(result, ""), nil
default:
return llvm.Value{}, errors.New("unknown binop type: " + binop.X.Type().String())
return llvm.Value{}, errors.New("unknown: binop on struct: " + op.String())
}
return result, nil
default:
return llvm.Value{}, errors.New("todo: binop type: " + typ.String())
}
}

43
testdata/binop.go предоставленный Обычный файл
Просмотреть файл

@ -0,0 +1,43 @@
package main
func main() {
// string equality
println(a == "a")
println(a == "b")
println(a != "a")
println(a != "b")
// struct equality
println(s1 == Struct1{3, true})
println(s1 == Struct1{4, true})
println(s1 == Struct1{3, false})
println(s1 == Struct1{4, false})
println(s1 != Struct1{3, true})
println(s1 != Struct1{4, true})
println(s1 != Struct1{3, false})
println(s1 != Struct1{4, false})
// blank fields in structs
println(s2 == Struct2{"foo", 0.0, 5})
println(s2 == Struct2{"foo", 0.0, 7})
println(s2 == Struct2{"foo", 1.0, 5})
println(s2 == Struct2{"foo", 1.0, 7})
}
var x = true
var y = false
var a = "a"
var s1 = Struct1{3, true}
var s2 = Struct2{"foo", 0.0, 5}
type Struct1 struct {
i int
b bool
}
type Struct2 struct {
s string
_ float64
i int
}

16
testdata/binop.txt предоставленный Обычный файл
Просмотреть файл

@ -0,0 +1,16 @@
true
false
false
true
true
false
false
false
false
true
true
true
true
false
true
false