From 82df5eccb7af2c1c99826c80902b2d3558a48366 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Thu, 27 Oct 2022 20:31:12 +0800 Subject: [PATCH] Implement translation of unary and binary operations. --- hello.wa | 10 +- .../compiler_llvm/compile_function.go | 11 +- .../backends/compiler_llvm/compile_value.go | 117 ++++++++++++++++++ internal/backends/compiler_llvm/utils.go | 50 +++++++- 4 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 internal/backends/compiler_llvm/compile_value.go diff --git a/hello.wa b/hello.wa index 25109d6..c265bfa 100644 --- a/hello.wa +++ b/hello.wa @@ -6,9 +6,13 @@ fn main { print('言') print('\n') - println(add(40, 2)) + println(calc0(42, 5, 3)) } -fn add(a: i32, b: i32) => i32 { - return a + b +fn calc0(a: i32, b: i32, c: i32) => i32 { + return a * 5 / (b - c + 1) +} + +fn calc2(a: float32, b: float32, c: float32) => float32 { + return a * 5 / (b - c + 1) } diff --git a/internal/backends/compiler_llvm/compile_function.go b/internal/backends/compiler_llvm/compile_function.go index 6b62bde..9bff011 100644 --- a/internal/backends/compiler_llvm/compile_function.go +++ b/internal/backends/compiler_llvm/compile_function.go @@ -54,18 +54,19 @@ func (p *Compiler) compileInstr(instr ssa.Instruction) error { case 0: p.output.WriteString(" ret void\n") case 1: // ret %type %value - p.output.WriteString(" ; ret ") + p.output.WriteString(" ret ") p.output.WriteString(getTypeStr(instr.Results[0].Type(), p.target)) - p.output.WriteString(" %" + instr.Results[0].Name()) - p.output.WriteString("\n") - p.output.WriteString(" ret " + getTypeStr(instr.Results[0].Type(), p.target) + " 1") + p.output.WriteString(" ") + p.output.WriteString(getValueStr(instr.Results[0])) p.output.WriteString("\n") default: return errors.New("multiple return values are not supported") } case ssa.Value: - p.output.WriteString(" ; " + instr.Name() + " = " + instr.String() + "\n") + if err := p.compileValue(instr); err != nil { + return err + } default: p.output.WriteString(" ; " + instr.String() + "\n") diff --git a/internal/backends/compiler_llvm/compile_value.go b/internal/backends/compiler_llvm/compile_value.go new file mode 100644 index 0000000..3e4b571 --- /dev/null +++ b/internal/backends/compiler_llvm/compile_value.go @@ -0,0 +1,117 @@ +// 版权 @2022 凹语言 作者。保留所有权利。 + +package compiler_llvm + +import ( + "github.com/wa-lang/wa/internal/ssa" + "github.com/wa-lang/wa/internal/token" +) + +func (p *Compiler) compileValue(val ssa.Value) error { + switch val := val.(type) { + case *ssa.UnOp: + if err := p.compileUnOp(val); err != nil { + return err + } + + case *ssa.BinOp: + if err := p.compileBinOp(val); err != nil { + return err + } + + case *ssa.Call: + if err := p.compileCall(val); err != nil { + return err + } + + default: + p.output.WriteString(" ; " + val.Name() + " = " + val.String() + "\n") + // panic("unsupported Value '" + val.Name() + " = " + val.String() + "'") + } + + return nil +} + +func (p *Compiler) compileUnOp(val *ssa.UnOp) error { + switch val.Op { + case token.SUB: + p.output.WriteString(" ") + p.output.WriteString("%" + val.Name()) + p.output.WriteString(" = ") + if isFloat, _ := checkType(val.X.Type()); isFloat { + p.output.WriteString("fneg ") + p.output.WriteString(getTypeStr(val.X.Type(), p.target)) + p.output.WriteString(" ") + } else { + p.output.WriteString("sub ") + p.output.WriteString(getTypeStr(val.X.Type(), p.target)) + p.output.WriteString(" 0, ") + } + p.output.WriteString(getValueStr(val.X)) + p.output.WriteString("\n") + + default: + p.output.WriteString(" ; " + val.Name() + " = " + val.String() + "\n") + // panic("unsupported Value '" + val.Name() + " = " + val.String() + "'") + } + + return nil +} + +func (p *Compiler) compileBinOp(val *ssa.BinOp) error { + sintOpMap := map[token.Token]string{ + token.ADD: "add", + token.SUB: "sub", + token.MUL: "mul", + token.QUO: "sdiv", + token.REM: "srem", + } + uintOpMap := map[token.Token]string{ + token.ADD: "add", + token.SUB: "sub", + token.MUL: "mul", + token.QUO: "udiv", + token.REM: "urem", + } + floatOpMap := map[token.Token]string{ + token.ADD: "fadd", + token.SUB: "fsub", + token.MUL: "fmul", + token.QUO: "fdiv", + } + + // Type float, signed int, unsigned int each has its own LLVM-IR. + var opMap map[token.Token]string + isFloat, isSigned := checkType(val.X.Type()) + if isFloat { + opMap = floatOpMap + } else if isSigned { + opMap = sintOpMap + } else { + opMap = uintOpMap + } + + if op, ok := opMap[val.Op]; ok { + p.output.WriteString(" ") + p.output.WriteString("%" + val.Name()) + p.output.WriteString(" = ") + p.output.WriteString(op) + p.output.WriteString(" ") + p.output.WriteString(getTypeStr(val.X.Type(), p.target)) + p.output.WriteString(" ") + p.output.WriteString(getValueStr(val.X)) + p.output.WriteString(", ") + p.output.WriteString(getValueStr(val.Y)) + p.output.WriteString("\n") + return nil + } + + p.output.WriteString(" ; " + val.Name() + " = " + val.String() + "\n") + return nil + // panic("unsupported Value '" + val.Name() + " = " + val.String() + "'") +} + +func (p *Compiler) compileCall(val *ssa.Call) error { + p.output.WriteString(" ; " + val.Name() + " = " + val.String() + "\n") + return nil +} diff --git a/internal/backends/compiler_llvm/utils.go b/internal/backends/compiler_llvm/utils.go index 80591c9..08b88f7 100644 --- a/internal/backends/compiler_llvm/utils.go +++ b/internal/backends/compiler_llvm/utils.go @@ -3,8 +3,11 @@ package compiler_llvm import ( + "fmt" + "strconv" "strings" + "github.com/wa-lang/wa/internal/ssa" "github.com/wa-lang/wa/internal/types" ) @@ -16,6 +19,26 @@ func getArch(arch string) string { return arch[0:pos] } +func checkType(from types.Type) (isFloat bool, isSigned bool) { + switch t := from.(type) { + case *types.Basic: + switch t.Kind() { + case types.Float32, types.Float64, types.UntypedFloat: + return true, true + case types.Uint8, types.Uint16, types.Uint32, types.Uint64, + types.Uint: + return false, false + case types.Bool, types.UntypedBool, types.Int8, types.Int16, + types.Int32, types.Int64, types.Int, types.UntypedInt: + return false, true + default: + panic("unknown basic type") + } + default: + panic("basic type is expected") + } +} + func getTypeStr(from types.Type, target string) string { // feasible types on different targets defInt := map[string]string{ @@ -41,9 +64,9 @@ func getTypeStr(from types.Type, target string) string { types.Uint32: "i32", types.Int64: "i64", types.Uint64: "i64", - types.Float32: "f32", - types.Float64: "f64", - types.UntypedFloat: "f32", + types.Float32: "float", + types.Float64: "double", + types.UntypedFloat: "float", } switch t := from.(type) { @@ -68,3 +91,24 @@ func getTypeStr(from types.Type, target string) string { panic("unknown type") } } + +func getValueStr(val ssa.Value) string { + switch val.(type) { + case *ssa.Const: + name := val.Name() + if pos := strings.Index(name, ":"); pos > 0 { + name = name[0:pos] + // Special form for float32/float64 constants as LLVM-IR requested. + if isFloat, _ := checkType(val.Type()); isFloat { + if f, err := strconv.ParseFloat(name, 64); err == nil { + name = fmt.Sprintf("%e", f) + } + } + } + return name + case *ssa.Parameter: + return "%" + val.Name() + default: + return "%" + val.Name() + } +} -- GitLab