compiler: implement comparing structs directly
Этот коммит содержится в:
родитель
cbd7d401fe
коммит
e2f6aedd9d
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
предоставленный
Обычный файл
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
предоставленный
Обычный файл
16
testdata/binop.txt
предоставленный
Обычный файл
|
@ -0,0 +1,16 @@
|
|||
true
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
false
|
||||
false
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
false
|
||||
true
|
||||
false
|
Загрузка…
Создание таблицы
Сослаться в новой задаче