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
|
return buf, nil
|
||||||
case *ssa.BinOp:
|
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:
|
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.
|
||||||
|
@ -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) {
|
func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) (llvm.Value, error) {
|
||||||
x, err := c.parseExpr(frame, binop.X)
|
switch typ := typ.(type) {
|
||||||
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) {
|
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
if typ.Info()&types.IsInteger != 0 {
|
if typ.Info()&types.IsInteger != 0 {
|
||||||
// Operations on integers
|
// Operations on integers
|
||||||
signed := typ.Info()&types.IsUnsigned == 0
|
signed := typ.Info()&types.IsUnsigned == 0
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.ADD: // +
|
case token.ADD: // +
|
||||||
return c.builder.CreateAdd(x, y, ""), nil
|
return c.builder.CreateAdd(x, y, ""), nil
|
||||||
case token.SUB: // -
|
case token.SUB: // -
|
||||||
|
@ -2888,7 +2888,7 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
|
||||||
// in Go.
|
// in Go.
|
||||||
y = c.builder.CreateTrunc(y, x.Type(), "")
|
y = c.builder.CreateTrunc(y, x.Type(), "")
|
||||||
}
|
}
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.SHL: // <<
|
case token.SHL: // <<
|
||||||
return c.builder.CreateShl(x, y, ""), nil
|
return c.builder.CreateShl(x, y, ""), nil
|
||||||
case token.SHR: // >>
|
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
|
return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil
|
||||||
}
|
}
|
||||||
default:
|
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 {
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
// Operations on floats
|
// Operations on floats
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.ADD:
|
case token.ADD:
|
||||||
return c.builder.CreateFAdd(x, y, ""), nil
|
return c.builder.CreateFAdd(x, y, ""), nil
|
||||||
case token.SUB: // -
|
case token.SUB: // -
|
||||||
|
@ -2961,41 +2961,41 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
|
||||||
case token.GEQ: // >=
|
case token.GEQ: // >=
|
||||||
return c.builder.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
|
return c.builder.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
|
||||||
default:
|
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 {
|
} else if typ.Info()&types.IsBoolean != 0 {
|
||||||
// Operations on booleans
|
// Operations on booleans
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
case token.NEQ: // !=
|
case token.NEQ: // !=
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||||
default:
|
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 {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
// Operations on pointers
|
// Operations on pointers
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
case token.NEQ: // !=
|
case token.NEQ: // !=
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||||
default:
|
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 {
|
} else if typ.Info()&types.IsString != 0 {
|
||||||
// Operations on strings
|
// Operations on strings
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.ADD:
|
case token.ADD:
|
||||||
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
|
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
|
||||||
case token.EQL, token.NEQ: // ==, !=
|
case token.EQL, token.NEQ: // ==, !=
|
||||||
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
|
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
|
||||||
if binop.Op == token.NEQ {
|
if op == token.NEQ {
|
||||||
result = c.builder.CreateNot(result, "")
|
result = c.builder.CreateNot(result, "")
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
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 {
|
} else {
|
||||||
return llvm.Value{}, errors.New("todo: unknown basic type in binop: " + typ.String())
|
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, "")
|
x = c.builder.CreateExtractValue(x, 1, "")
|
||||||
y = c.builder.CreateExtractValue(y, 1, "")
|
y = c.builder.CreateExtractValue(y, 1, "")
|
||||||
}
|
}
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
case token.NEQ: // !=
|
case token.NEQ: // !=
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||||
default:
|
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:
|
case *types.Interface:
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL, token.NEQ: // ==, !=
|
case token.EQL, token.NEQ: // ==, !=
|
||||||
result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
|
result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
|
||||||
if binop.Op == token.NEQ {
|
if op == token.NEQ {
|
||||||
result = c.builder.CreateNot(result, "")
|
result = c.builder.CreateNot(result, "")
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
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:
|
case *types.Pointer:
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
case token.NEQ: // !=
|
case token.NEQ: // !=
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||||
default:
|
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:
|
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 := c.builder.CreateExtractValue(x, 0, "")
|
||||||
yPtr := c.builder.CreateExtractValue(y, 0, "")
|
yPtr := c.builder.CreateExtractValue(y, 0, "")
|
||||||
switch binop.Op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil
|
||||||
case token.NEQ: // !=
|
case token.NEQ: // !=
|
||||||
return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
|
return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil
|
||||||
default:
|
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 on struct: " + op.String())
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, errors.New("unknown binop type: " + binop.X.Type().String())
|
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
|
Загрузка…
Создание таблицы
Сослаться в новой задаче