Add support for floats
I'm not sure all operations are supported but the vast majority should be supported now. This commit also refactors binop translation.
Этот коммит содержится в:
родитель
3cdf606183
коммит
46d2d2e777
2 изменённых файлов: 194 добавлений и 106 удалений
|
@ -37,7 +37,7 @@ func main() {
|
||||||
Currently supported features:
|
Currently supported features:
|
||||||
|
|
||||||
* control flow
|
* control flow
|
||||||
* many (but not all) basic types: most ints, strings, structs
|
* many (but not all) basic types: most ints, floats, strings, structs
|
||||||
* function calling
|
* function calling
|
||||||
* interfaces for basic types (with type switches and asserts)
|
* interfaces for basic types (with type switches and asserts)
|
||||||
* goroutines (very initial support)
|
* goroutines (very initial support)
|
||||||
|
@ -50,7 +50,7 @@ Currently supported features:
|
||||||
|
|
||||||
Not yet supported:
|
Not yet supported:
|
||||||
|
|
||||||
* float, complex, etc.
|
* complex numbers
|
||||||
* garbage collection
|
* garbage collection
|
||||||
* defer
|
* defer
|
||||||
* closures
|
* closures
|
||||||
|
|
168
compiler.go
168
compiler.go
|
@ -455,6 +455,10 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
|
||||||
return c.intType, nil
|
return c.intType, nil
|
||||||
case types.Int64, types.Uint64:
|
case types.Int64, types.Uint64:
|
||||||
return llvm.Int64Type(), nil
|
return llvm.Int64Type(), nil
|
||||||
|
case types.Float32:
|
||||||
|
return llvm.FloatType(), nil
|
||||||
|
case types.Float64:
|
||||||
|
return llvm.DoubleType(), nil
|
||||||
case types.String:
|
case types.String:
|
||||||
return c.mod.GetTypeByName("runtime._string"), nil
|
return c.mod.GetTypeByName("runtime._string"), nil
|
||||||
case types.Uintptr:
|
case types.Uintptr:
|
||||||
|
@ -573,6 +577,8 @@ func getZeroValue(typ llvm.Type) (llvm.Value, error) {
|
||||||
vals[i] = val
|
vals[i] = val
|
||||||
}
|
}
|
||||||
return llvm.ConstArray(subTyp, vals), nil
|
return llvm.ConstArray(subTyp, vals), nil
|
||||||
|
case llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
|
return llvm.ConstFloat(typ, 0.0), nil
|
||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
return llvm.ConstInt(typ, 0, false), nil
|
return llvm.ConstInt(typ, 0, false), nil
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
|
@ -1853,20 +1859,14 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
typ := binop.X.Type().Underlying()
|
switch typ := binop.X.Type().Underlying().(type) {
|
||||||
signed := false
|
case *types.Basic:
|
||||||
if typ, ok := typ.(*types.Basic); ok {
|
if typ.Info()&types.IsInteger != 0 {
|
||||||
signed = typ.Info()&types.IsUnsigned == 0
|
// Operations on integers
|
||||||
}
|
signed := typ.Info()&types.IsUnsigned == 0
|
||||||
switch binop.Op {
|
switch binop.Op {
|
||||||
case token.ADD: // +
|
case token.ADD: // +
|
||||||
if typ, ok := binop.X.Type().(*types.Basic); ok && typ.Kind() == types.String {
|
|
||||||
// string concatenation
|
|
||||||
fn := c.mod.NamedFunction("runtime.stringConcat")
|
|
||||||
return c.builder.CreateCall(fn, []llvm.Value{x, y}, ""), nil
|
|
||||||
} else {
|
|
||||||
return c.builder.CreateAdd(x, y, ""), nil
|
return c.builder.CreateAdd(x, y, ""), nil
|
||||||
}
|
|
||||||
case token.SUB: // -
|
case token.SUB: // -
|
||||||
return c.builder.CreateSub(x, y, ""), nil
|
return c.builder.CreateSub(x, y, ""), nil
|
||||||
case token.MUL: // *
|
case token.MUL: // *
|
||||||
|
@ -1909,37 +1909,14 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
case token.EQL: // ==
|
||||||
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
|
case token.NEQ: // !=
|
||||||
|
return c.builder.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 := c.builder.CreateNot(y, "") // ~y
|
||||||
return c.builder.CreateAnd(x, inv, ""), nil
|
return c.builder.CreateAnd(x, inv, ""), nil
|
||||||
case token.EQL, token.NEQ: // ==, !=
|
|
||||||
switch typ := binop.X.Type().Underlying().(type) {
|
|
||||||
case *types.Basic:
|
|
||||||
if typ.Info()&types.IsInteger != 0 || typ.Kind() == types.UnsafePointer {
|
|
||||||
if binop.Op == token.EQL {
|
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
|
||||||
} else {
|
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
|
||||||
}
|
|
||||||
} else if typ.Kind() == types.String {
|
|
||||||
result := c.builder.CreateCall(c.mod.NamedFunction("runtime.stringEqual"), []llvm.Value{x, y}, "")
|
|
||||||
if binop.Op == token.NEQ {
|
|
||||||
result = c.builder.CreateNot(result, "")
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
} else {
|
|
||||||
return llvm.Value{}, errors.New("todo: equality operator on unknown basic type: " + typ.String())
|
|
||||||
}
|
|
||||||
case *types.Pointer:
|
|
||||||
if binop.Op == token.EQL {
|
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
|
||||||
} else {
|
|
||||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, errors.New("todo: equality operator on unknown type: " + typ.String())
|
|
||||||
}
|
|
||||||
case token.LSS: // <
|
case token.LSS: // <
|
||||||
if signed {
|
if signed {
|
||||||
return c.builder.CreateICmp(llvm.IntSLT, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntSLT, x, y, ""), nil
|
||||||
|
@ -1965,7 +1942,75 @@ 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("unknown binop")
|
return llvm.Value{}, errors.New("todo: binop on integer: " + binop.Op.String())
|
||||||
|
}
|
||||||
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
|
// Operations on floats
|
||||||
|
switch binop.Op {
|
||||||
|
case token.ADD:
|
||||||
|
return c.builder.CreateFAdd(x, y, ""), nil
|
||||||
|
case token.SUB: // -
|
||||||
|
return c.builder.CreateFSub(x, y, ""), nil
|
||||||
|
case token.MUL: // *
|
||||||
|
return c.builder.CreateFMul(x, y, ""), nil
|
||||||
|
case token.QUO: // /
|
||||||
|
return c.builder.CreateFDiv(x, y, ""), nil
|
||||||
|
case token.REM: // %
|
||||||
|
return c.builder.CreateFRem(x, y, ""), nil
|
||||||
|
case token.EQL: // ==
|
||||||
|
return c.builder.CreateFCmp(llvm.FloatOEQ, x, y, ""), nil
|
||||||
|
case token.NEQ: // !=
|
||||||
|
return c.builder.CreateFCmp(llvm.FloatONE, x, y, ""), nil
|
||||||
|
case token.LSS: // <
|
||||||
|
return c.builder.CreateFCmp(llvm.FloatOLT, x, y, ""), nil
|
||||||
|
case token.LEQ: // <=
|
||||||
|
return c.builder.CreateFCmp(llvm.FloatOLE, x, y, ""), nil
|
||||||
|
case token.GTR: // >
|
||||||
|
return c.builder.CreateFCmp(llvm.FloatOGT, x, y, ""), nil
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
|
// Operations on pointers
|
||||||
|
switch binop.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())
|
||||||
|
}
|
||||||
|
} else if typ.Kind() == types.String {
|
||||||
|
// Operations on strings
|
||||||
|
switch binop.Op {
|
||||||
|
case token.ADD:
|
||||||
|
fn := c.mod.NamedFunction("runtime.stringConcat")
|
||||||
|
return c.builder.CreateCall(fn, []llvm.Value{x, y}, ""), nil
|
||||||
|
case token.EQL, token.NEQ: // ==, !=
|
||||||
|
result := c.builder.CreateCall(c.mod.NamedFunction("runtime.stringEqual"), []llvm.Value{x, y}, "")
|
||||||
|
if binop.Op == token.NEQ {
|
||||||
|
result = c.builder.CreateNot(result, "")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, errors.New("todo: binop on string: " + binop.Op.String())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return llvm.Value{}, errors.New("todo: unknown basic type in binop: " + typ.String())
|
||||||
|
}
|
||||||
|
case *types.Pointer:
|
||||||
|
switch binop.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())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, errors.New("unknown binop type: " + binop.X.Type().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2005,6 +2050,9 @@ func (c *Compiler) parseConst(expr *ssa.Const) (llvm.Value, error) {
|
||||||
} else if typ.Info()&types.IsInteger != 0 { // signed
|
} else if typ.Info()&types.IsInteger != 0 { // signed
|
||||||
n, _ := constant.Int64Val(expr.Value)
|
n, _ := constant.Int64Val(expr.Value)
|
||||||
return llvm.ConstInt(llvmType, uint64(n), true), nil
|
return llvm.ConstInt(llvmType, uint64(n), true), nil
|
||||||
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
|
n, _ := constant.Float64Val(expr.Value)
|
||||||
|
return llvm.ConstFloat(llvmType, n), nil
|
||||||
} else {
|
} else {
|
||||||
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
|
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
|
||||||
}
|
}
|
||||||
|
@ -2111,6 +2159,7 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (
|
||||||
|
|
||||||
typeFrom := typeFrom.Underlying().(*types.Basic)
|
typeFrom := typeFrom.Underlying().(*types.Basic)
|
||||||
sizeTo := c.targetData.TypeAllocSize(llvmTypeTo)
|
sizeTo := c.targetData.TypeAllocSize(llvmTypeTo)
|
||||||
|
|
||||||
if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 {
|
if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 {
|
||||||
// Conversion between two integers.
|
// Conversion between two integers.
|
||||||
if sizeFrom > sizeTo {
|
if sizeFrom > sizeTo {
|
||||||
|
@ -2122,7 +2171,36 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return llvm.Value{}, errors.New("todo: convert: basic non-integer type: " + typeTo.String())
|
if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsFloat != 0 {
|
||||||
|
// Conversion between two floats.
|
||||||
|
if sizeFrom > sizeTo {
|
||||||
|
return c.builder.CreateFPTrunc(value, llvmTypeTo, ""), nil
|
||||||
|
} else if sizeFrom < sizeTo {
|
||||||
|
return c.builder.CreateFPExt(value, llvmTypeTo, ""), nil
|
||||||
|
} else {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsInteger != 0 {
|
||||||
|
// Conversion from float to int.
|
||||||
|
if typeTo.Info()&types.IsUnsigned != 0 { // to signed int
|
||||||
|
return c.builder.CreateFPToSI(value, llvmTypeTo, ""), nil
|
||||||
|
} else { // to unsigned int
|
||||||
|
return c.builder.CreateFPToUI(value, llvmTypeTo, ""), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsFloat != 0 {
|
||||||
|
// Conversion from int to float.
|
||||||
|
if typeFrom.Info()&types.IsUnsigned != 0 { // from signed int
|
||||||
|
return c.builder.CreateSIToFP(value, llvmTypeTo, ""), nil
|
||||||
|
} else { // from unsigned int
|
||||||
|
return c.builder.CreateUIToFP(value, llvmTypeTo, ""), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return llvm.Value{}, errors.New("todo: convert: basic non-integer type: " + typeFrom.String() + " -> " + typeTo.String())
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, errors.New("todo: convert " + typeTo.String() + " <- " + typeFrom.String())
|
return llvm.Value{}, errors.New("todo: convert " + typeTo.String() + " <- " + typeFrom.String())
|
||||||
|
@ -2187,7 +2265,17 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||||
case token.NOT: // !x
|
case token.NOT: // !x
|
||||||
return c.builder.CreateNot(x, ""), nil
|
return c.builder.CreateNot(x, ""), nil
|
||||||
case token.SUB: // -x
|
case token.SUB: // -x
|
||||||
|
if typ, ok := unop.X.Type().Underlying().(*types.Basic); ok {
|
||||||
|
if typ.Info()&types.IsInteger != 0 {
|
||||||
return c.builder.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil
|
return c.builder.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil
|
||||||
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
|
return c.builder.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil
|
||||||
|
} else {
|
||||||
|
return llvm.Value{}, errors.New("todo: unknown basic type for negate: " + typ.String())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return llvm.Value{}, errors.New("todo: unknown type for negate: " + unop.X.Type().Underlying().String())
|
||||||
|
}
|
||||||
case token.MUL: // *x, dereference pointer
|
case token.MUL: // *x, dereference pointer
|
||||||
valType := unop.X.Type().(*types.Pointer).Elem()
|
valType := unop.X.Type().(*types.Pointer).Elem()
|
||||||
if valType, ok := valType.(*types.Named); ok && valType.Obj().Name() == "__reg" {
|
if valType, ok := valType.(*types.Named); ok && valType.Obj().Name() == "__reg" {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче