diff --git a/internal/backends/compiler_wasm/compile.go b/internal/backends/compiler_wasm/compile.go new file mode 100644 index 0000000000000000000000000000000000000000..9fca225ebfeb46a6e62e6c1329b0b716966b5df8 --- /dev/null +++ b/internal/backends/compiler_wasm/compile.go @@ -0,0 +1,95 @@ +// 版权 @2021 凹语言 作者。保留所有权利。 + +package compiler_wasm + +import ( + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir" + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/loader" + "github.com/wa-lang/wa/internal/ssa" +) + +type Compiler struct { + ssaPkg *ssa.Package + + module wir.Module +} + +func New() *Compiler { + p := new(Compiler) + return p +} + +func (p *Compiler) Compile(prog *loader.Program) (output string, err error) { + p.CompilePackage(prog.SSAMainPkg) + + return p.String(), nil +} + +func (p *Compiler) CompilePackage(ssaPkg *ssa.Package) { + + p.ssaPkg = ssaPkg + + var ts []*ssa.Type + var cs []*ssa.NamedConst + var gs []*ssa.Global + var fns []*ssa.Function + + { + var sig wir.FuncSig + sig.Params = append(sig.Params, wtypes.Int32{}) + p.module.Imports = append(p.module.Imports, wir.NewImpFunc("js", "print_i32", "$$print_i32", sig)) + p.module.Imports = append(p.module.Imports, wir.NewImpFunc("js", "print_char", "$$print_char", sig)) + } + + for _, m := range p.ssaPkg.Members { + switch member := m.(type) { + case *ssa.Type: + ts = append(ts, member) + case *ssa.NamedConst: + cs = append(cs, member) + case *ssa.Global: + gs = append(gs, member) + case *ssa.Function: + //fns = append(fns, member) + default: + panic("Unreachable") + } + } + + for _, v := range ts { + p.compileType(v) + } + + for _, v := range cs { + p.compileConst(v) + } + + for _, v := range gs { + p.compileGlobal(v) + } + + for _, v := range ssaPkg.GetValues() { + if f, ok := v.(*ssa.Function); ok { + found := false + for _, m := range fns { + if m.Object() == f.Object() { + found = true + } + } + if found { + continue + } + fns = append(fns, f) + } + } + for _, v := range fns { + p.module.Funcs = append(p.module.Funcs, newFunctionGenerator(p).genFunction(v)) + } + + println(p.module.String()) +} + +func (p *Compiler) String() string { + return "" +} diff --git a/internal/backends/compiler_wasm/compile_const.go b/internal/backends/compiler_wasm/compile_const.go new file mode 100644 index 0000000000000000000000000000000000000000..aaa95dddde1b266bec230ce6e45cd1d0ac3b0a41 --- /dev/null +++ b/internal/backends/compiler_wasm/compile_const.go @@ -0,0 +1,12 @@ +// 版权 @2021 凹语言 作者。保留所有权利。 + +package compiler_wasm + +import ( + "github.com/wa-lang/wa/internal/logger" + "github.com/wa-lang/wa/internal/ssa" +) + +func (p *Compiler) compileConst(c *ssa.NamedConst) { + logger.Fatal("Todo") +} diff --git a/internal/backends/compiler_wasm/compile_func.go b/internal/backends/compiler_wasm/compile_func.go new file mode 100644 index 0000000000000000000000000000000000000000..c05a6f8733e85761dddd29689510d22049fb5946 --- /dev/null +++ b/internal/backends/compiler_wasm/compile_func.go @@ -0,0 +1,437 @@ +// 版权 @2021 凹语言 作者。保留所有权利。 + +package compiler_wasm + +import ( + "strconv" + + "github.com/wa-lang/wa/internal/constant" + "github.com/wa-lang/wa/internal/token" + "github.com/wa-lang/wa/internal/types" + + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir" + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" + "github.com/wa-lang/wa/internal/ssa" +) + +type functionGenerator struct { + module *wir.Module + + locals_map map[ssa.Value]wir.Value + + registers []wir.Value + cur_local_id int + + var_block_selector wir.Value + var_current_block wir.Value + var_ret wir.Value +} + +func newFunctionGenerator(p *Compiler) *functionGenerator { + return &functionGenerator{module: &p.module, locals_map: make(map[ssa.Value]wir.Value)} +} + +func (g *functionGenerator) getValue(i ssa.Value) wir.Value { + if i == nil { + return nil + } + + if v, ok := g.locals_map[i]; ok { + return v + } + + switch v := i.(type) { + case *ssa.Const: + switch t := v.Type().(type) { + case *types.Basic: + switch t.Kind() { + case types.Bool: + logger.Fatalf("Todo:%T", t) + + case types.Int: + val, _ := constant.Int64Val(v.Value) + return wir.NewConstI32(int32(val)) + + case types.Float32: + logger.Fatalf("Todo:%T", t) + + case types.Float64: + logger.Fatalf("Todo:%T", t) + + case types.String, types.UntypedString: + logger.Fatalf("Todo:%T", t) + + default: + logger.Fatalf("Todo:%T", t) + } + + case *types.Slice: + logger.Fatalf("Todo:%T", t) + + default: + logger.Fatalf("Todo:%T", t) + } + + case ssa.Instruction: + nv := g.addRegister(wir.ToWType(i.Type())) + g.locals_map[i] = nv + return nv + } + + logger.Fatal("Value not found:", i) + return nil +} + +func (g *functionGenerator) genFunction(f *ssa.Function) *wir.Function { + var wir_fn wir.Function + wir_fn.Name = f.Name() + + rets := f.Signature.Results() + wir_fn.Result = wir.ToWType(rets) + if rets.Len() > 1 { + logger.Fatal("Todo") + } + + for _, i := range f.Params { + pa := wir.NewVar(i.Name(), wir.ValueKindLocal, wir.ToWType(i.Type())) + wir_fn.Params = append(wir_fn.Params, pa) + g.locals_map[i] = pa + } + + g.var_block_selector = wir.NewVar("$$block_selector", wir.ValueKindLocal, wtypes.Int32{}) + g.registers = append(g.registers, g.var_block_selector) + wir_fn.Insts = append(wir_fn.Insts, wir.EmitAssginValue(g.var_block_selector, nil)...) + + g.var_current_block = wir.NewVar("$$current_block", wir.ValueKindLocal, wtypes.Int32{}) + g.registers = append(g.registers, g.var_current_block) + wir_fn.Insts = append(wir_fn.Insts, wir.EmitAssginValue(g.var_current_block, nil)...) + + if !wir_fn.Result.Equal(wtypes.Void{}) { + g.var_ret = wir.NewVar("$$ret", wir.ValueKindLocal, wir_fn.Result) + g.registers = append(g.registers, g.var_ret) + wir_fn.Insts = append(wir_fn.Insts, wir.EmitAssginValue(g.var_ret, nil)...) + } + + var block_temp wir.Instruction + //BlockSel: + { + inst := wir.NewInstBlock("$$BlockSel") + inst.Insts = append(inst.Insts, wir.EmitPushValue(g.var_block_selector)...) + t := make([]int, len(f.Blocks)+1) + for i := range f.Blocks { + t[i] = i + } + t[len(f.Blocks)] = 0 + inst.Insts = append(inst.Insts, wir.NewInstBrTable(t)) + block_temp = inst + } + + for i, b := range f.Blocks { + block := wir.NewInstBlock("$$Block_" + strconv.Itoa(i)) + block.Insts = append(block.Insts, block_temp) + block.Insts = append(block.Insts, g.genBlock(b)...) + block_temp = block + } + + //BlockDisp + { + inst := wir.NewInstLoop("$$BlockDisp") + inst.Insts = append(inst.Insts, block_temp) + block_temp = inst + } + + //BlockFnBody + { + inst := wir.NewInstBlock("$$BlockFnBody") + inst.Insts = append(inst.Insts, block_temp) + block_temp = inst + } + + wir_fn.Insts = append(wir_fn.Insts, block_temp) + wir_fn.Locals = g.registers + + return &wir_fn +} + +func (g *functionGenerator) genBlock(block *ssa.BasicBlock) []wir.Instruction { + if len(block.Instrs) == 0 { + logger.Fatalf("Block:%s is empty", block) + } + + cur_block_assigned := false + var b []wir.Instruction + for _, inst := range block.Instrs { + if _, ok := inst.(*ssa.Phi); !ok { + if !cur_block_assigned { + b = append(b, wir.EmitAssginValue(g.var_current_block, wir.NewConstI32(int32(block.Index)))...) + cur_block_assigned = true + } + } + + b = append(b, g.genInstruction(inst)...) + } + return b + +} + +func (g *functionGenerator) genInstruction(inst ssa.Instruction) []wir.Instruction { + switch inst := inst.(type) { + + case *ssa.Alloc: + logger.Fatalf("Todo:%T", inst) + + case *ssa.If: + return g.genIf(inst) + + case *ssa.Store: + logger.Fatalf("Todo:%T", inst) + + case *ssa.Jump: + return g.genJump(inst) + + case *ssa.Return: + return g.genReturn(inst) + + case *ssa.Extract: + logger.Fatalf("Todo:%T", inst) + + case *ssa.Field: + logger.Fatalf("Todo:%T", inst) + + case ssa.Value: + s, t := g.genValue(inst) + if !t.Equal(wtypes.Void{}) { + v := g.getValue(inst) + s = append(s, wir.EmitPopValue(v)...) + g.locals_map[inst] = v + } + return s + + default: + logger.Fatal("Todo:", inst.String()) + } + return nil +} + +func (g *functionGenerator) genValue(v ssa.Value) ([]wir.Instruction, wtypes.ValueType) { + if _, ok := g.locals_map[v]; ok { + logger.Printf("Instruction already exist:%s\n", v) + } + + switch v := v.(type) { + case *ssa.UnOp: + logger.Fatalf("Todo: %v, type: %T", v, v) + + case *ssa.BinOp: + return g.genBinOp(v) + + case *ssa.Call: + return g.genCall(v) + + case *ssa.Phi: + return g.genPhi(v) + + case *ssa.FieldAddr: + logger.Fatalf("Todo: %v, type: %T", v, v) + + case *ssa.IndexAddr: + logger.Fatalf("Todo: %v, type: %T", v, v) + + case *ssa.Slice: + logger.Fatalf("Todo: %v, type: %T", v, v) + } + + logger.Fatalf("Todo: %v, type: %T", v, v) + return nil, nil +} + +func (g *functionGenerator) genBinOp(inst *ssa.BinOp) ([]wir.Instruction, wtypes.ValueType) { + x := g.getValue(inst.X) + y := g.getValue(inst.Y) + + switch inst.X.Type().Underlying().(type) { + case *types.Basic: + switch inst.Op { + case token.ADD: + return wir.EmitBinOp(x, y, wir.OpCodeAdd) + + case token.SUB: + return wir.EmitBinOp(x, y, wir.OpCodeSub) + + case token.MUL: + return wir.EmitBinOp(x, y, wir.OpCodeMul) + + case token.QUO: + return wir.EmitBinOp(x, y, wir.OpCodeQuo) + + case token.REM: + return wir.EmitBinOp(x, y, wir.OpCodeRem) + + case token.EQL: + return wir.EmitBinOp(x, y, wir.OpCodeEql) + + case token.NEQ: + return wir.EmitBinOp(x, y, wir.OpCodeNe) + + case token.LSS: + return wir.EmitBinOp(x, y, wir.OpCodeLt) + + case token.GTR: + return wir.EmitBinOp(x, y, wir.OpCodeGt) + + case token.LEQ: + return wir.EmitBinOp(x, y, wir.OpCodeLe) + + case token.GEQ: + return wir.EmitBinOp(x, y, wir.OpCodeGe) + } + + default: + logger.Fatalf("Todo: %v, type: %T, token:%v", inst, inst, inst.Op) + } + + logger.Fatalf("Todo: %v, type: %T, token:%v", inst, inst, inst.Op) + return nil, nil +} + +func (g *functionGenerator) genCall(inst *ssa.Call) ([]wir.Instruction, wtypes.ValueType) { + if inst.Call.IsInvoke() { + logger.Fatal("Todo: genCall(), Invoke") + } + + switch inst.Call.Value.(type) { + case *ssa.Function: + ret_type := wir.ToWType(inst.Call.Signature().Results()) + var insts []wir.Instruction + for _, v := range inst.Call.Args { + insts = append(insts, wir.EmitPushValue(g.getValue(v))...) + } + insts = append(insts, wir.NewInstCall(inst.Call.StaticCallee().Name())) + return insts, ret_type + + case *ssa.Builtin: + return g.genBuiltin(inst.Common()) + + case *ssa.MakeClosure: + logger.Fatal("Todo: genCall(), MakeClosure") + + default: + logger.Fatalf("Todo: type:%T", inst.Call.Value) + } + + logger.Fatal("Todo") + + return nil, nil +} + +func (g *functionGenerator) genBuiltin(call *ssa.CallCommon) ([]wir.Instruction, wtypes.ValueType) { + switch call.Value.Name() { + case "print", "println": + var insts []wir.Instruction + for _, arg := range call.Args { + arg := g.getValue(arg) + switch arg.Type().(type) { + case wtypes.Int32: + insts = append(insts, wir.EmitPushValue(arg)...) + insts = append(insts, wir.NewInstCall("$$print_i32")) + + default: + logger.Fatalf("Todo: print(%s)", arg.Type().Name()) + } + } + + if call.Value.Name() == "println" { + insts = append(insts, wir.EmitPushValue(wir.NewConstI32('\n'))...) + insts = append(insts, wir.NewInstCall("$$print_char")) + } + + return insts, wtypes.Void{} + } + logger.Fatal("Todo:", call.Value) + return nil, nil +} + +func (g *functionGenerator) genPhiIter(preds []int, values []wir.Value) []wir.Instruction { + var insts []wir.Instruction + + cond, _ := wir.EmitBinOp(g.var_current_block, wir.NewConstI32(int32(preds[0])), wir.OpCodeEql) + insts = append(insts, cond...) + + trueInsts := append([]wir.Instruction(nil), wir.EmitPushValue(values[0])...) + var falseInsts []wir.Instruction + if len(preds) == 2 { + falseInsts = append([]wir.Instruction(nil), wir.EmitPushValue(values[1])...) + } else { + falseInsts = append([]wir.Instruction(nil), g.genPhiIter(preds[1:], values[1:])...) + } + insts = append(insts, wir.NewInstIf(trueInsts, falseInsts, values[0].Type())) + + return insts +} +func (g *functionGenerator) genPhi(inst *ssa.Phi) ([]wir.Instruction, wtypes.ValueType) { + var preds []int + var values []wir.Value + for i, v := range inst.Edges { + preds = append(preds, inst.Block().Preds[i].Index) + values = append(values, g.getValue(v)) + } + return g.genPhiIter(preds, values), wir.ToWType(inst.Type()) +} + +func (g *functionGenerator) genReturn(inst *ssa.Return) []wir.Instruction { + var insts []wir.Instruction + + switch len(inst.Results) { + case 0: + break + + case 1: + insts = append(insts, wir.EmitAssginValue(g.var_ret, g.getValue(inst.Results[0]))...) + + default: + logger.Fatal("Todo") + } + + insts = append(insts, wir.NewInstBr("$$BlockFnBody")) + return insts +} + +func (g *functionGenerator) genIf(inst *ssa.If) []wir.Instruction { + cond := g.getValue(inst.Cond) + if !cond.Type().Equal(wtypes.Int32{}) { + logger.Fatal("cond.type() != i32") + } + + insts := wir.EmitPushValue(cond) + instsTrue := g.genJumpID(inst.Block().Index, inst.Block().Succs[0].Index) + instsFalse := g.genJumpID(inst.Block().Index, inst.Block().Succs[1].Index) + insts = append(insts, wir.NewInstIf(instsTrue, instsFalse, wtypes.Void{})) + + return insts +} + +func (g *functionGenerator) genJump(inst *ssa.Jump) []wir.Instruction { + return g.genJumpID(inst.Block().Index, inst.Block().Succs[0].Index) +} + +func (g *functionGenerator) genJumpID(cur, dest int) []wir.Instruction { + var insts []wir.Instruction + + if cur >= dest { + insts = wir.EmitAssginValue(g.var_block_selector, wir.NewConstI32(int32(dest))) + insts = append(insts, wir.NewInstBr("$$BlockDisp")) + } else { + insts = append(insts, wir.NewInstBr("$$Block_"+strconv.Itoa(dest-1))) + } + + return insts +} + +func (g *functionGenerator) addRegister(typ wtypes.ValueType) wir.Value { + defer func() { g.cur_local_id++ }() + name := "$$T_" + strconv.Itoa(g.cur_local_id) + v := wir.NewVar(name, wir.ValueKindLocal, typ) + g.registers = append(g.registers, v) + return v +} diff --git a/internal/backends/compiler_wasm/compile_global.go b/internal/backends/compiler_wasm/compile_global.go new file mode 100644 index 0000000000000000000000000000000000000000..f72c92c83076f56398b57b01d2899f0d8f821419 --- /dev/null +++ b/internal/backends/compiler_wasm/compile_global.go @@ -0,0 +1,11 @@ +// 版权 @2021 凹语言 作者。保留所有权利。 + +package compiler_wasm + +import ( + "github.com/wa-lang/wa/internal/ssa" +) + +func (p *Compiler) compileGlobal(g *ssa.Global) { + //logger.Fatal("Todo") +} diff --git a/internal/backends/compiler_wasm/compile_type.go b/internal/backends/compiler_wasm/compile_type.go new file mode 100644 index 0000000000000000000000000000000000000000..f6e9a55d9cecc13b911c05bdf00184f1fcfe592e --- /dev/null +++ b/internal/backends/compiler_wasm/compile_type.go @@ -0,0 +1,12 @@ +// 版权 @2021 凹语言 作者。保留所有权利。 + +package compiler_wasm + +import ( + "github.com/wa-lang/wa/internal/logger" + "github.com/wa-lang/wa/internal/ssa" +) + +func (p *Compiler) compileType(t *ssa.Type) { + logger.Fatal("Todo") +} diff --git a/internal/backends/compiler_wasm/wir/funcion.go b/internal/backends/compiler_wasm/wir/funcion.go new file mode 100644 index 0000000000000000000000000000000000000000..4835a88641b1a2e893ef403edccbcae1c62a8cd3 --- /dev/null +++ b/internal/backends/compiler_wasm/wir/funcion.go @@ -0,0 +1,58 @@ +package wir + +import "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + +func (f *Function) Format(indent string) string { + s := indent + "(func $" + f.Name + " (export \"" + f.Name + "\")" + + for _, param := range f.Params { + rps := param.Raw() + for _, rp := range rps { + s += " (param " + rp.Name() + " " + rp.Type().Name() + ")" + } + } + + if !f.Result.Equal(wtypes.Void{}) { + s += " (result" + rrs := f.Result.Raw() + for _, rr := range rrs { + s += " " + rr.Name() + } + s += ")" + } + s += "\n" + + for _, local := range f.Locals { + rls := local.Raw() + s += indent + " " + for _, rl := range rls { + s += " (local " + rl.Name() + " " + rl.Type().Name() + ")" + } + s += "\n" + } + + for _, inst := range f.Insts { + s += inst.Format(indent+" ") + "\n" + } + + s += indent + ") ;;" + f.Name + return s +} + +func (sig *FuncSig) String() string { + str := "" + for _, param := range sig.Params { + rps := param.Raw() + for _, rp := range rps { + str += " (param " + rp.Name() + ")" + } + } + + for _, ret := range sig.Results { + rrs := ret.Raw() + for _, rp := range rrs { + str += " (result " + rp.Name() + ")" + } + } + return str +} diff --git a/internal/backends/compiler_wasm/wir/import.go b/internal/backends/compiler_wasm/wir/import.go new file mode 100644 index 0000000000000000000000000000000000000000..053f135d03f7f54cff5fda01d66af62898bc5c32 --- /dev/null +++ b/internal/backends/compiler_wasm/wir/import.go @@ -0,0 +1,29 @@ +package wir + +/************************************** +ImpObj: +**************************************/ +type ImpObj struct { + moduleName string + objName string +} + +func (o *ImpObj) ModuleName() string { return o.moduleName } +func (o *ImpObj) ObjName() string { return o.objName } + +/************************************** +ImpFunc: +**************************************/ +type ImpFunc struct { + ImpObj + funcName string + sig FuncSig +} + +func NewImpFunc(moduleName string, objName string, funcName string, sig FuncSig) *ImpFunc { + return &ImpFunc{ImpObj: ImpObj{moduleName: moduleName, objName: objName}, funcName: funcName, sig: sig} +} +func (o *ImpFunc) Type() ObjType { return ObjTypeFunc } +func (o *ImpFunc) Format(indent string) string { + return "(import \"" + o.moduleName + "\" \"" + o.objName + "\" (func " + o.funcName + o.sig.String() + "))" +} diff --git a/internal/backends/compiler_wasm/wir/instruction.go b/internal/backends/compiler_wasm/wir/instruction.go new file mode 100644 index 0000000000000000000000000000000000000000..0bdeafc8d4e14c9a91943cf1267efb5d5af806be --- /dev/null +++ b/internal/backends/compiler_wasm/wir/instruction.go @@ -0,0 +1,350 @@ +package wir + +import ( + "strconv" + + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" +) + +type anInstruction struct { +} + +func (i *anInstruction) isInstruction() {} + +/************************************** +InstConst: +**************************************/ +type InstConst struct { + anInstruction + value Value +} + +func NewInstConst(v Value) *InstConst { + if v.Kind() != ValueKindConst { + logger.Fatal("newInstructionConst()只能接受常数") + } + return &InstConst{value: v} +} +func (i *InstConst) Format(indent string) string { + switch i.value.Type().(type) { + case wtypes.Int32: + return indent + "i32.const " + i.value.Name() + + case wtypes.Int64: + return indent + "i64.const " + i.value.Name() + } + + logger.Fatalf("Todo %T", i.value.Type()) + return "" +} + +/************************************** +InstGetLocal: +**************************************/ +type InstGetLocal struct { + anInstruction + name string +} + +func NewInstGetLocal(name string) *InstGetLocal { return &InstGetLocal{name: name} } +func (i *InstGetLocal) Format(indent string) string { return indent + "local.get " + i.name } + +/************************************** +instSetLocal: +**************************************/ +type InstSetLocal struct { + anInstruction + name string +} + +func NewInstSetLocal(name string) *InstSetLocal { return &InstSetLocal{name: name} } +func (i *InstSetLocal) Format(indent string) string { return indent + "local.set " + i.name } + +/************************************** +InstAdd: +**************************************/ +type InstAdd struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstAdd(t wtypes.ValueType) *InstAdd { return &InstAdd{typ: t} } +func (i *InstAdd) Format(indent string) string { return indent + i.typ.String() + ".add" } + +/************************************** +InstSub: +**************************************/ +type InstSub struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstSub(t wtypes.ValueType) *InstSub { return &InstSub{typ: t} } +func (i *InstSub) Format(indent string) string { return indent + i.typ.String() + ".sub" } + +/************************************** +InstMul: +**************************************/ +type InstMul struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstMul(t wtypes.ValueType) *InstMul { return &InstMul{typ: t} } +func (i *InstMul) Format(indent string) string { return indent + i.typ.String() + ".mul" } + +/************************************** +InstDiv: +**************************************/ +type InstDiv struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstDiv(t wtypes.ValueType) *InstDiv { return &InstDiv{typ: t} } +func (i *InstDiv) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.div_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstRem: +**************************************/ +type InstRem struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstRem(t wtypes.ValueType) *InstRem { return &InstRem{typ: t} } +func (i *InstRem) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.rem_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstEq: +**************************************/ +type InstEq struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstEq(t wtypes.ValueType) *InstEq { return &InstEq{typ: t} } +func (i *InstEq) Format(indent string) string { return indent + i.typ.String() + ".eq" } + +/************************************** +InstNe: +**************************************/ +type InstNe struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstNe(t wtypes.ValueType) *InstNe { return &InstNe{typ: t} } +func (i *InstNe) Format(indent string) string { return indent + i.typ.String() + ".ne" } + +/************************************** +InstLt: +**************************************/ +type InstLt struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstLt(t wtypes.ValueType) *InstLt { return &InstLt{typ: t} } +func (i *InstLt) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.lt_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstGt: +**************************************/ +type InstGt struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstGt(t wtypes.ValueType) *InstGt { return &InstGt{typ: t} } +func (i *InstGt) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.gt_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstLe: +**************************************/ +type InstLe struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstLe(t wtypes.ValueType) *InstLe { return &InstLe{typ: t} } +func (i *InstLe) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.le_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstGe: +**************************************/ +type InstGe struct { + anInstruction + typ wtypes.ValueType +} + +func NewInstGe(t wtypes.ValueType) *InstGe { return &InstGe{typ: t} } +func (i *InstGe) Format(indent string) string { + switch i.typ.(type) { + case wtypes.Int32: + return indent + "i32.ge_s" + } + logger.Fatal("Todo") + return "" +} + +/************************************** +InstCall: +**************************************/ +type InstCall struct { + anInstruction + name string +} + +func NewInstCall(name string) *InstCall { return &InstCall{name: name} } +func (i *InstCall) Format(indent string) string { return indent + "call " + i.name } + +/************************************** +InstBlock: +**************************************/ +type InstBlock struct { + anInstruction + name string + Insts []Instruction +} + +func NewInstBlock(name string) *InstBlock { return &InstBlock{name: name} } +func (i *InstBlock) Format(indent string) string { + s := indent + "(block " + s += i.name + "\n" + for _, v := range i.Insts { + s += v.Format(indent+" ") + "\n" + } + s += indent + ") ;;" + i.name + return s +} + +/************************************** +InstLoop: +**************************************/ +type InstLoop struct { + anInstruction + name string + Insts []Instruction +} + +func NewInstLoop(name string) *InstLoop { return &InstLoop{name: name} } +func (i *InstLoop) Format(indent string) string { + s := indent + "(loop " + s += i.name + "\n" + for _, v := range i.Insts { + s += v.Format(indent+" ") + "\n" + } + s += indent + ") ;;" + i.name + return s +} + +/************************************** +InstBr: +**************************************/ +type InstBr struct { + anInstruction + Name string +} + +func NewInstBr(name string) *InstBr { return &InstBr{Name: name} } +func (i *InstBr) Format(indent string) string { return indent + "br " + i.Name } + +/************************************** +InstBrTable: +**************************************/ +type InstBrTable struct { + anInstruction + Table []int +} + +func NewInstBrTable(t []int) *InstBrTable { return &InstBrTable{Table: t} } +func (i *InstBrTable) Format(indent string) string { + s := indent + "br_table" + for _, v := range i.Table { + s += " " + strconv.Itoa(v) + } + return s +} + +/************************************** +InstIf: +**************************************/ +type InstIf struct { + anInstruction + True []Instruction + False []Instruction + Ret wtypes.ValueType +} + +func NewInstIf(instsTrue, instsFalse []Instruction, ret wtypes.ValueType) *InstIf { + return &InstIf{True: instsTrue, False: instsFalse, Ret: ret} +} +func (i *InstIf) Format(indent string) string { + s := indent + "if" + if !i.Ret.Equal(wtypes.Void{}) { + s += " (result" + rrs := i.Ret.Raw() + for _, rr := range rrs { + s += " " + rr.Name() + } + s += ")" + } + s += "\n" + + for _, v := range i.True { + s += v.Format(indent+" ") + "\n" + } + s += indent + "else\n" + for _, v := range i.False { + s += v.Format(indent+" ") + "\n" + } + s += indent + "end" + return s +} + +/************************************** +InstReturn: +**************************************/ +type InstReturn struct { + anInstruction +} + +func NewInstReturn() *InstReturn { return &InstReturn{} } +func (i *InstReturn) Format(indent string) string { return indent + "return" } diff --git a/internal/backends/compiler_wasm/wir/instruction_emitter.go b/internal/backends/compiler_wasm/wir/instruction_emitter.go new file mode 100644 index 0000000000000000000000000000000000000000..84f844787d7c0675bde9c9f4be0a53ba48415b7d --- /dev/null +++ b/internal/backends/compiler_wasm/wir/instruction_emitter.go @@ -0,0 +1,140 @@ +package wir + +import ( + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" +) + +func EmitPushValue(x Value) []Instruction { + var insts []Instruction + vs := x.Raw() + if len(vs) > 1 { + for _, v := range vs { + insts = append(insts, EmitPushValue(v)...) + } + } + + switch x.Kind() { + case ValueKindConst: + insts = append(insts, NewInstConst(x)) + + case ValueKindLocal: + insts = append(insts, NewInstGetLocal(x.Name())) + + case ValueKindGlobal: + logger.Fatal("Todo") + } + + return insts +} + +func EmitPopValue(x Value) []Instruction { + var insts []Instruction + vs := x.Raw() + if len(vs) > 1 { + for _, v := range vs { + insts = append(insts, EmitPopValue(v)...) + } + } + + switch x.Kind() { + case ValueKindConst: + logger.Fatal("不可Pop至常数") + + case ValueKindLocal: + insts = append(insts, NewInstSetLocal(x.Name())) + + case ValueKindGlobal: + logger.Fatal("Todo") + } + + return insts +} + +func EmitAssginValue(lh, rh Value) []Instruction { + var insts []Instruction + + if rh == nil { + ls := lh.Raw() + for _, v := range ls { + c := NewConst(v.Type(), nil) + insts = append(insts, EmitPushValue(c)...) + insts = append(insts, EmitPopValue(v)...) + } + } else { + if !lh.Type().Equal(rh.Type()) { + logger.Fatal("x.Type() != y.Type()") + } + + ls := lh.Raw() + rs := rh.Raw() + + for i := range ls { + insts = append(insts, EmitPushValue(rs[i])...) + insts = append(insts, EmitPopValue(ls[i])...) + } + } + + return insts +} + +func EmitConvertValueType(from, to wtypes.ValueType) { + logger.Fatal("Todo") +} + +func EmitBinOp(x, y Value, op OpCode) ([]Instruction, wtypes.ValueType) { + var insts []Instruction + rtype := binOpMatchType(x.Type(), y.Type()) + + insts = append(insts, EmitPushValue(x)...) + insts = append(insts, EmitPushValue(y)...) + + switch op { + case OpCodeAdd: + insts = append(insts, NewInstAdd(rtype)) + + case OpCodeSub: + insts = append(insts, NewInstSub(rtype)) + + case OpCodeMul: + insts = append(insts, NewInstMul(rtype)) + + case OpCodeQuo: + insts = append(insts, NewInstDiv(rtype)) + + case OpCodeRem: + insts = append(insts, NewInstRem(rtype)) + + case OpCodeEql: + insts = append(insts, NewInstEq(rtype)) + + case OpCodeNe: + insts = append(insts, NewInstNe(rtype)) + + case OpCodeLt: + insts = append(insts, NewInstLt(rtype)) + + case OpCodeGt: + insts = append(insts, NewInstGt(rtype)) + + case OpCodeLe: + insts = append(insts, NewInstLe(rtype)) + + case OpCodeGe: + insts = append(insts, NewInstGe(rtype)) + + default: + logger.Fatal("Todo") + } + + return insts, rtype +} + +func binOpMatchType(x, y wtypes.ValueType) wtypes.ValueType { + if x.Equal(y) { + return x + } + + logger.Fatalf("Todo %T %T", x, y) + return nil +} diff --git a/internal/backends/compiler_wasm/wir/module.go b/internal/backends/compiler_wasm/wir/module.go new file mode 100644 index 0000000000000000000000000000000000000000..8e8a88cb325048e86f8b8d14bc06e2c2b456a3ce --- /dev/null +++ b/internal/backends/compiler_wasm/wir/module.go @@ -0,0 +1,16 @@ +package wir + +func (m *Module) String() string { + s := "(module\n" + + for _, i := range m.Imports { + s += i.Format(" ") + "\n" + } + + for _, f := range m.Funcs { + s += f.Format(" ") + "\n" + } + + s += ") ;;module" + return s +} diff --git a/internal/backends/compiler_wasm/wir/util.go b/internal/backends/compiler_wasm/wir/util.go new file mode 100644 index 0000000000000000000000000000000000000000..8199631e78532dd9b07008b5e9090e514a0c7799 --- /dev/null +++ b/internal/backends/compiler_wasm/wir/util.go @@ -0,0 +1,72 @@ +package wir + +import ( + "github.com/wa-lang/wa/internal/types" + + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" +) + +func ToWType(from types.Type) wtypes.ValueType { + switch t := from.(type) { + case *types.Basic: + switch t.Kind() { + case types.Bool: + return wtypes.Int32{} + + case types.Int8, types.UntypedBool: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Uint8: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Int16: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Uint16: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Int, types.Int32, types.UntypedInt: + return wtypes.Int32{} + + case types.Uint, types.Uint32: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Int64: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Uint64: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Float32, types.UntypedFloat: + logger.Fatalf("ToWType Todo:%T", t) + + case types.Float64: + logger.Fatalf("ToWType Todo:%T", t) + + case types.String: + logger.Fatalf("ToWType Todo:%T", t) + + default: + logger.Fatalf("Unknown type:%s", t) + return nil + } + + case *types.Tuple: + switch t.Len() { + case 0: + return wtypes.Void{} + + case 1: + return ToWType(t.At(0).Type()) + + default: + logger.Fatalf("Todo type:%s", t) + } + + default: + logger.Fatalf("Todo:%T", t) + } + + return nil +} diff --git a/internal/backends/compiler_wasm/wir/value_const.go b/internal/backends/compiler_wasm/wir/value_const.go new file mode 100644 index 0000000000000000000000000000000000000000..b2268edf3175191fb063068fd5f5a3abf76189d9 --- /dev/null +++ b/internal/backends/compiler_wasm/wir/value_const.go @@ -0,0 +1,80 @@ +package wir + +import ( + "strconv" + + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" +) + +type Const interface { + Value +} + +/************************************** +ConstZero: +**************************************/ +/*type ConstZero struct { +} + +func NewConstZero() *ConstZero { return &ConstZero{} } +func (c *ConstZero) Name() string { return "0" } +func (c *ConstZero) Kind() ValueKind { return ValueKindConst } +func (c *ConstZero) Type() wtypes.ValueType { return wtypes.Void{} } +func (c *ConstZero) Raw() []Value { return append([]Value(nil), c) } +//*/ + +func NewConst(t wtypes.ValueType, v interface{}) Const { + switch t.(type) { + case wtypes.Int32: + if v == nil { + return NewConstI32(0) + } + + if c, ok := v.(int); ok { + return NewConstI32(int32(c)) + } + logger.Fatal("Todo") + + case wtypes.Int64: + if v == nil { + return NewConstI64(0) + } + + if c, ok := v.(int); ok { + return NewConstI64(int64(c)) + } + logger.Fatal("Todo") + + default: + logger.Fatal("Todo") + } + + return nil +} + +/************************************** +ConstInt32: +**************************************/ +type ConstI32 struct { + x int32 +} + +func NewConstI32(x int32) *ConstI32 { return &ConstI32{x: x} } +func (c *ConstI32) Name() string { return strconv.FormatInt(int64(c.x), 10) } +func (c *ConstI32) Kind() ValueKind { return ValueKindConst } +func (c *ConstI32) Type() wtypes.ValueType { return wtypes.Int32{} } +func (c *ConstI32) Raw() []Value { return append([]Value(nil), c) } + +/************************************** +ConstInt64: +**************************************/ +type ConstI64 struct { + x int64 +} + +func NewConstI64(x int64) *ConstI64 { return &ConstI64{x: x} } +func (c *ConstI64) Name() string { return strconv.FormatInt(c.x, 10) } +func (c *ConstI64) Kind() ValueKind { return ValueKindConst } +func (c *ConstI64) Type() wtypes.ValueType { return wtypes.Int64{} } +func (c *ConstI64) Raw() []Value { return append([]Value(nil), c) } diff --git a/internal/backends/compiler_wasm/wir/value_var.go b/internal/backends/compiler_wasm/wir/value_var.go new file mode 100644 index 0000000000000000000000000000000000000000..82f54f280b774bcd992717a6df9af78899fd2bde --- /dev/null +++ b/internal/backends/compiler_wasm/wir/value_var.go @@ -0,0 +1,32 @@ +package wir + +import ( + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" + "github.com/wa-lang/wa/internal/logger" +) + +func NewVar(name string, kind ValueKind, typ wtypes.ValueType) Value { + switch typ.(type) { + case wtypes.Int32: + return &VarI32{name: name, kind: kind, typ: typ} + + default: + logger.Fatalf("Todo: %T", typ) + } + + return nil +} + +/************************************** +VarI32: +**************************************/ +type VarI32 struct { + name string + kind ValueKind + typ wtypes.ValueType +} + +func (v *VarI32) Name() string { return v.name } +func (v *VarI32) Kind() ValueKind { return v.kind } +func (v *VarI32) Type() wtypes.ValueType { return v.typ } +func (v *VarI32) Raw() []Value { return append([]Value(nil), v) } diff --git a/internal/backends/compiler_wasm/wir/wir.go b/internal/backends/compiler_wasm/wir/wir.go new file mode 100644 index 0000000000000000000000000000000000000000..3058038ff5f7e78234d5a3a11e25ca8529d9c128 --- /dev/null +++ b/internal/backends/compiler_wasm/wir/wir.go @@ -0,0 +1,97 @@ +package wir + +import ( + "github.com/wa-lang/wa/internal/backends/compiler_wasm/wir/wtypes" +) + +type Module struct { + Imports []Import + Funcs []*Function +} + +/************************************** +Import: +**************************************/ +type Import interface { + Format(indent string) string + ModuleName() string + ObjName() string + Type() ObjType +} + +/************************************** +Function: +**************************************/ +type Function struct { + Name string + Result wtypes.ValueType + Params []Value + Locals []Value + + Insts []Instruction +} + +/************************************** +FuncSig: +**************************************/ +type FuncSig struct { + Params []wtypes.ValueType + Results []wtypes.ValueType +} + +/************************************** +Instruction: +**************************************/ +type Instruction interface { + Format(indent string) string + isInstruction() +} + +type ValueKind uint8 + +const ( + ValueKindLocal ValueKind = iota + ValueKindGlobal + ValueKindConst +) + +/************************************** +Value: +**************************************/ +type Value interface { + Name() string + Kind() ValueKind + Type() wtypes.ValueType + Raw() []Value +} + +/************************************** +OpCode: +**************************************/ +type OpCode int32 + +const ( + OpCodeAdd OpCode = iota + OpCodeSub + OpCodeMul + OpCodeQuo + OpCodeRem + OpCodeEql + OpCodeNe + OpCodeLt + OpCodeGt + OpCodeLe + OpCodeGe +) + +/************************************** +ObjType: +**************************************/ +type ObjType int32 + +const ( + ObjTypeFunc ObjType = iota + ObjTypeMem + ObjTypeTable + ObjTypeGlobal +) diff --git a/internal/backends/compiler_wasm/wir/wtypes/wtypes.go b/internal/backends/compiler_wasm/wir/wtypes/wtypes.go new file mode 100644 index 0000000000000000000000000000000000000000..a4b1a4bd27e7534ac8fabc483088fa0ba0028a1a --- /dev/null +++ b/internal/backends/compiler_wasm/wir/wtypes/wtypes.go @@ -0,0 +1,82 @@ +package wtypes + +import "fmt" + +type ValueType interface { + fmt.Stringer + Name() string + + GetByteSize() int + Raw() []ValueType + Equal(ValueType) bool +} + +/************************************** +Void: +**************************************/ +type Void struct { +} + +func (t Void) String() string { return t.Name() } +func (t Void) Name() string { return "void" } +func (t Void) GetByteSize() int { return 0 } +func (t Void) Raw() []ValueType { return append([]ValueType(nil), t) } +func (t Void) Equal(u ValueType) bool { + if _, ok := u.(Void); ok { + return true + } + return false +} + +/************************************** +Int32: +**************************************/ +type Int32 struct { +} + +func (t Int32) String() string { return t.Name() } +func (t Int32) Name() string { return "i32" } +func (t Int32) GetByteSize() int { return 4 } +func (t Int32) Raw() []ValueType { return append([]ValueType(nil), t) } +func (t Int32) Equal(u ValueType) bool { + if _, ok := u.(Int32); ok { + return true + } + return false +} + +/************************************** +Int64: +**************************************/ +type Int64 struct { +} + +func (t Int64) String() string { return t.Name() } +func (t Int64) Name() string { return "i64" } +func (t Int64) GetByteSize() int { return 8 } +func (t Int64) Raw() []ValueType { return append([]ValueType(nil), t) } +func (t Int64) Equal(u ValueType) bool { + if _, ok := u.(Int64); ok { + return true + } + return false +} + +/************************************** +Pointer: +**************************************/ +type Pointer struct { + Base ValueType +} + +func NewPointer(base ValueType) Pointer { return Pointer{Base: base} } +func (t Pointer) String() string { return "*" + t.Base.Name() } +func (t Pointer) Name() string { return "Todo" } +func (t Pointer) GetByteSize() int { return 4 } +func (t Pointer) Raw() []ValueType { return append([]ValueType(nil), Int32{}) } +func (t Pointer) Equal(u ValueType) bool { + if ut, ok := u.(Pointer); ok { + return t.Base.Equal(ut.Base) + } + return false +}