func checkBranchToEntry(mod *ssa.Module) error { makeError := func(i ssa.Instruction) error { return &InstrError{ Instr: i, Message: fmt.Sprintf("Branch to entry block `%%%s`", i.Block().Name()), } } for _, fn := range mod.Functions() { for _, block := range fn.Blocks() { lastInstr := block.LastInstr() switch i := lastInstr.(type) { case *ssa.Br: if block := ssa.GetOperands(i)[0].(*ssa.Block); block.IsEntry() { return makeError(i) } case *ssa.CondBr: for _, op := range ssa.GetOperands(i)[1:3] { if op.(*ssa.Block).IsEntry() { return makeError(i) } } } } } return nil }
func (v Target) genCall(a *allocator, instr *ssa.Call) { v.genLoadCallArguments(a, instr) v.wop("call %s", ssa.GetOperands(instr)[0].Name()) if v.Platform == platform.Windows { // TODO move this totalMem := winTotalMemSizeBits(ssa.GetOperands(instr)[0].Type().(*types.Signature).Parameters()) if totalMem > 0 { v.wop("addq $%d, #rsp", totalMem/8) } } v.genSaveReturnValue(a, instr) }
func (v Target) genGEP(a *allocator, instr *ssa.GEP) { ops := ssa.GetOperands(instr) val := ops[0] indexes := ops[1:] typ := val.Type() v.moveIntToReg(a, val, "rax") for _, index := range indexes { switch styp := typ.(type) { case *types.Pointer: v.handleGEPPointerOrArray(a, index, TypeStoreSizeInBits(styp.Element())) typ = styp.Element() case *types.Array: v.handleGEPPointerOrArray(a, index, TypeStoreSizeInBits(styp.Element())) typ = styp.Element() case *types.Struct: i := index.(*ssa.IntLiteral).LiteralValue().(uint64) v.wop("addq $%d, #rax", newStructLayout(styp).fieldOffsetBits(int(i))/8) typ = styp.Fields()[i] default: panic("unim") } } v.wop("movq #rax, %s", a.valStr(instr)) }
func checkBr(instr *ssa.Br) error { ops := ssa.GetOperands(instr) if err := errIfNotLabelType(instr, ops[0].Type()); err != nil { return err } return nil }
func (v Target) genIntBinOp(a *allocator, instr *ssa.BinOp, opIndex int) { ops := ssa.GetOperands(instr) sz := TypeSizeInBits(ops[0].Type()) suffix := sizeSuffixBits(sz) rax := regToSize("rax", sz) rcx := regToSize("rcx", sz) v.moveIntToReg(a, ops[0], rax) v.moveIntToReg(a, ops[1], rcx) switch instr.BinOpType() { case ssa.BinOpAdd, ssa.BinOpSub, ssa.BinOpAnd, ssa.BinOpOr, ssa.BinOpXor: v.wop("%s%s #%s, #%s", binOpIntOpStrs[opIndex], suffix, rcx, rax) case ssa.BinOpMul: v.wop("mul%s #%s", suffix, rcx) case ssa.BinOpShl, ssa.BinOpAShr, ssa.BinOpLShr: v.wop("%s%s #cl, #%s", binOpIntOpStrs[opIndex], suffix, rax) default: panic("unim") } if sz == 1 { v.wop("andq $1, #%s", rax) } v.wop("mov%s #%s, %s", suffix, rax, a.valStr(instr)) }
func (v Target) genBr(a *allocator, instr *ssa.Br, blockLabelMap map[*ssa.Block]string) { target := ssa.GetOperands(instr)[0].(*ssa.Block) v.handleBrPhi(a, instr.Block(), target) v.wop("jmp %s", blockLabelMap[target]) }
func checkInstr(instr ssa.Instruction, blockDomTree *analysis.DominatorTree) error { thisBlockNode := blockDomTree.NodeForBlock(instr.Block()) if _, ok := instr.(*ssa.Phi); !ok { for _, op := range ssa.GetOperands(instr) { if opInstr, ok := op.(ssa.Instruction); ok { opInstrBlock := opInstr.Block() if thisBlockNode.DominatedBy(blockDomTree.NodeForBlock(opInstrBlock), true) { continue } else if opInstrBlock == instr.Block() { if opInstrBlock.InstrIndex(opInstr) < opInstrBlock.InstrIndex(instr) { continue } } return &InstrError{ Instr: instr, Message: "Instruction is not dominated by operand `" + ssa.ValueString(op) + "`", } } } } switch i := instr.(type) { case *ssa.BinOp: return checkBinOp(i) case *ssa.Load: return checkLoad(i) case *ssa.Call: return checkCall(i) case *ssa.Alloc: return checkAlloc(i) case *ssa.Store: return checkStore(i) case *ssa.Convert: return checkConvert(i) case *ssa.ICmp: return checkICmp(i) case *ssa.CondBr: return checkCondBr(i) case *ssa.Br: return checkBr(i) case *ssa.Phi: return checkPhi(i, blockDomTree) case *ssa.Ret: return checkRet(i) case *ssa.GEP: return checkGEP(i) case *ssa.Unreachable: // do nothing default: panic("unim") } return nil }
func (v Target) genRet(a *allocator, instr *ssa.Ret) { retVal := ssa.GetOperands(instr)[0] v.genLoadReturnValue(a, retVal) v.wop("movq #rbp, #rsp") v.wop("popq #r15") v.wop("popq #rbx") v.wop("popq #rbp") v.wop("retq") }
func (v Target) genLoadCallArguments(a *allocator, call *ssa.Call) { ops := ssa.GetOperands(call) if v.Platform.IsUnixLike() { v.sysVCopyFunctionVals(a, ops[1:], ops[0].Type().(*types.Signature), true) } else if v.Platform == platform.Windows { v.winLoadCallArguments(a, ops[1:], ops[0].Type().(*types.Signature)) } else { panic("unim") } }
func checkICmp(instr *ssa.ICmp) error { ops := ssa.GetOperands(instr) if err := errIfMismatchedTypes(ops[0].Type(), ops[1].Type(), instr); err != nil { return err } else if err := errIfNotIntType(instr, ops[0].Type()); err != nil { return err } return nil }
func (v *CFG) construct(fn *ssa.Function) { blocks := fn.Blocks() nodes := make([]*CFGNode, len(blocks)) blocksToNodes := make(map[*ssa.Block]*CFGNode) for i, block := range blocks { nodes[i] = &CFGNode{ block: block, } blocksToNodes[block] = nodes[i] } for _, node := range nodes { switch term := node.block.LastInstr().(type) { case *ssa.Br: ops := ssa.GetOperands(term) node.next = []*CFGNode{ blocksToNodes[ops[0].(*ssa.Block)], } case *ssa.CondBr: ops := ssa.GetOperands(term) node.next = []*CFGNode{ blocksToNodes[ops[1].(*ssa.Block)], blocksToNodes[ops[2].(*ssa.Block)], } case *ssa.Unreachable, *ssa.Ret: // these lead to nowhere default: panic("unimplemented terminating instruction") } for _, next := range node.next { next.prev = append(next.prev, node) } } v.nodes = nodes }
func (v Target) genCondBr(a *allocator, instr *ssa.CondBr, blockLabelMap map[*ssa.Block]string) { ops := ssa.GetOperands(instr) trueTarget := ops[1].(*ssa.Block) falseTarget := ops[2].(*ssa.Block) v.handleBrPhi(a, instr.Block(), trueTarget) v.handleBrPhi(a, instr.Block(), falseTarget) v.moveIntToReg(a, ops[0], "al") v.wop("testb #al, #al") v.wop("jne %s", blockLabelMap[trueTarget]) v.wop("je %s", blockLabelMap[falseTarget]) }
func checkBinOp(instr *ssa.BinOp) error { ops := ssa.GetOperands(instr) if err := errIfMismatchedTypes(ops[0].Type(), ops[1].Type(), instr); err != nil { return err } switch instr.BinOpType() { case ssa.BinOpAdd, ssa.BinOpSub, ssa.BinOpMul, ssa.BinOpSDiv, ssa.BinOpUDiv, ssa.BinOpSRem, ssa.BinOpURem, ssa.BinOpShl, ssa.BinOpLShr, ssa.BinOpAShr, ssa.BinOpAnd, ssa.BinOpOr, ssa.BinOpXor: _, ok := ops[0].Type().(*types.Int) if !ok { return &InstrError{ Instr: instr, Message: "`" + instr.BinOpType().String() + "` requires int", } } case ssa.BinOpFAdd, ssa.BinOpFSub, ssa.BinOpFMul, ssa.BinOpFDiv, ssa.BinOpFRem: _, ok := ops[0].Type().(*types.Float) if !ok { return &InstrError{ Instr: instr, Message: "`" + instr.BinOpType().String() + "` requires float", } } default: panic("unim") } return nil }
func checkGEP(instr *ssa.GEP) error { ops := ssa.GetOperands(instr) instrErr := func(message string) error { return &InstrError{ Instr: instr, Message: message, } } value := ops[0] indexes := ops[1:] typ := value.Type() for i, index := range indexes { switch styp := typ.(type) { case *types.Pointer: if i != 0 { return instrErr("Index " + fmt.Sprintf("%d", i) + " dereferences a pointer (only the index 0 may dereference a pointer)") } typ = styp.Element() case *types.Array: typ = styp.Element() case *types.Struct: lit, ok := index.(*ssa.IntLiteral) if !ok { return instrErr("Expected int literal at index " + fmt.Sprintf("%d", i)) } if int(lit.LiteralValue().(uint64)) >= len(styp.Fields()) { return instrErr("Index " + fmt.Sprintf("%d", i) + " has value greater than number of struct fields") } typ = styp.Fields()[lit.LiteralValue().(uint64)] default: return instrErr("Index " + fmt.Sprintf("%d", i) + " is invalid") } } return nil }
func checkRet(instr *ssa.Ret) error { fnReturnType := instr.Block().Function().Type().(*types.Signature).ReturnType() ops := ssa.GetOperands(instr) if ops[0] == nil { if _, ok := fnReturnType.(types.Void); !ok { return &InstrError{ Instr: instr, Message: "Expected return value of type `" + fnReturnType.String() + "`", } } } else if err := errIfMismatchedTypes(ops[0].Type(), fnReturnType, instr); err != nil { return err } return nil }
func (v Target) genICmp(a *allocator, instr *ssa.ICmp) { ops := ssa.GetOperands(instr) v.wop("xorq #rcx, #rcx") v.wop("movq $1, #rdx") sz := TypeStoreSizeInBits(ops[0].Type()) rax := regToSize("rax", sz) rbx := regToSize("rbx", sz) v.moveIntToReg(a, ops[1], rbx) v.moveIntToReg(a, ops[0], rax) v.wop("cmp%s #%s, #%s", sizeSuffixBits(sz), rbx, rax) moveType := "" switch instr.Predicate() { case ssa.IntEQ: moveType = "e" case ssa.IntNEQ: moveType = "ne" case ssa.IntUGT: moveType = "a" // above case ssa.IntUGE: moveType = "ae" // above or equal case ssa.IntULT: moveType = "b" // below case ssa.IntULE: moveType = "be" // below or equal case ssa.IntSGT: moveType = "g" // greater case ssa.IntSGE: moveType = "ge" // greater or equal case ssa.IntSLT: moveType = "l" // less case ssa.IntSLE: moveType = "le" // less or equal default: panic("unimplemented int predicate") } v.wop("cmov%sq #rdx, #rcx", moveType) v.wop("movb #cl, %s", a.valStr(instr)) }
func checkStore(instr *ssa.Store) error { ops := ssa.GetOperands(instr) ptr, ok := ops[0].Type().(*types.Pointer) if !ok { return &InstrError{ Instr: instr, Message: "Expected pointer type, found `" + ops[0].Type().String() + "`", } } if err := errIfMismatchedTypes(ptr.Element(), ops[1].Type(), instr); err != nil { return err } else if err := errIfNonFirstClassType(ops[1].Type(), instr); err != nil { return err } return nil }
func checkLoad(instr *ssa.Load) error { op := ssa.GetOperands(instr)[0] ptr, ok := op.Type().(*types.Pointer) if !ok { return &InstrError{ Instr: instr, Message: "Expected pointer type, found `" + instr.Type().String() + "`", } } if !types.IsFirstClass(ptr.Element()) { return &InstrError{ Instr: instr, Message: "Pointer element type is not first class", } } return nil }
func checkCall(instr *ssa.Call) error { ops := ssa.GetOperands(instr) sig, ok := ops[0].Type().(*types.Signature) if !ok { return &InstrError{ Instr: instr, Message: "Expected function type, found `" + ops[0].Type().String() + "`", } } fn := ops[0].(*ssa.Function) if len(ops)-1 > len(sig.Parameters()) { if !sig.Variadic() { return &InstrError{ Instr: instr, Message: "Too many arguments to function `" + ssa.ValueIdentifier(fn) + "`", } } } else if len(ops)-1 < len(sig.Parameters()) { return &InstrError{ Instr: instr, Message: "Too few arguments to function `" + ssa.ValueIdentifier(fn) + "`", } } for _, arg := range ops[1:] { if err := errIfNonFirstClassType(arg.Type(), instr); err != nil { return err } } for i, par := range sig.Parameters() { if err := errIfMismatchedTypes(ops[i+1].Type(), par, instr); err != nil { return err } } return nil }
func checkCondBr(instr *ssa.CondBr) error { ops := ssa.GetOperands(instr) if err := errIfNotIntType(instr, ops[0].Type()); err != nil { return err } if !ops[0].Type().Equals(types.NewInt(1)) { return &InstrError{ Instr: instr, Message: "Expected type i1, found `" + ops[0].Type().String() + "`", } } for _, target := range ops[1:] { if err := errIfNotLabelType(instr, target.Type()); err != nil { return err } } return nil }
func (v Target) genConvert(a *allocator, instr *ssa.Convert) { op := ssa.GetOperands(instr)[0] checkTypeSupported(op.Type()) checkTypeSupported(instr.Type()) targetStoresz := TypeStoreSizeInBits(instr.Type()) targetsz := TypeSizeInBits(instr.Type()) opsz := TypeSizeInBits(op.Type()) switch instr.ConvertType() { case ssa.ConvertBitcast: v.moveValToVal(a, op, instr) case ssa.ConvertTrunc: v.moveIntToReg(a, op, "rax") if !isRegSizeBits(targetsz) { v.wop("andq $%d, #rax", (1<<uint(targetsz))-1) } v.wop("mov%s #%s, %s", sizeSuffixBits(targetStoresz), regToSize("rax", targetStoresz), a.valStr(instr)) case ssa.ConvertZExt: v.moveIntToReg(a, op, "rax") v.moveRegToVal(a, "rax", instr) case ssa.ConvertSExt: v.genSExt(a, instr, op, opsz, targetStoresz, targetsz) case ssa.ConvertIntToPtr, ssa.ConvertPtrToInt: v.moveIntToReg(a, op, "rax") v.moveRegToVal(a, "rax", instr) default: panic("unim") } }
// this function is bad func checkConvert(instr *ssa.Convert) error { ops := ssa.GetOperands(instr) srcType := ops[0].Type() destType := instr.Type() mustBeInt := make([]types.Type, 0, 2) mustBeFloat := make([]types.Type, 0, 2) mustBePointer := make([]types.Type, 0, 2) switch instr.ConvertType() { case ssa.ConvertSExt, ssa.ConvertZExt, ssa.ConvertTrunc: mustBeInt = append(mustBeInt, srcType, destType) case ssa.ConvertBitcast: mustBePointer = append(mustBePointer, srcType, destType) case ssa.ConvertFExt, ssa.ConvertFTrunc: mustBeFloat = append(mustBeFloat, srcType, destType) case ssa.ConvertFToUI, ssa.ConvertFToSI: mustBeFloat = append(mustBeFloat, srcType) mustBeInt = append(mustBeInt, destType) case ssa.ConvertUIToF, ssa.ConvertSIToF: mustBeFloat = append(mustBeFloat, destType) mustBeInt = append(mustBeInt, srcType) case ssa.ConvertPtrToInt: mustBePointer = append(mustBePointer, srcType) mustBeInt = append(mustBeInt, destType) case ssa.ConvertIntToPtr: mustBeInt = append(mustBeInt, srcType) mustBePointer = append(mustBePointer, destType) default: panic("unim") } for _, t := range mustBeInt { if err := errIfNotIntType(instr, t); err != nil { return err } } for _, t := range mustBeFloat { if err := errIfNotFloatType(instr, t); err != nil { return err } } for _, t := range mustBePointer { if err := errIfNotPointerType(instr, t); err != nil { return err } } instrError := func(message string) error { return &InstrError{ Instr: instr, Message: message, } } switch instr.ConvertType() { case ssa.ConvertSExt, ssa.ConvertZExt: if srcType.(*types.Int).Width() >= destType.(*types.Int).Width() { return instrError("sext/zext requires src width < dest width") } case ssa.ConvertTrunc: if srcType.(*types.Int).Width() <= destType.(*types.Int).Width() { return instrError("trunc requires src width > dest width") } case ssa.ConvertBitcast: // do nothing case ssa.ConvertFExt: if srcType.(*types.Float).Type().CanExtendTo(destType.(*types.Float).Type()) { return instrError("fext cannot convert from " + srcType.String() + " to " + destType.String()) } case ssa.ConvertFTrunc: if srcType.(*types.Float).Type().CanTruncateTo(destType.(*types.Float).Type()) { return instrError("ftrunc cannot convert from " + srcType.String() + " to " + destType.String()) } case ssa.ConvertFToUI, ssa.ConvertFToSI, ssa.ConvertUIToF, ssa.ConvertSIToF, ssa.ConvertPtrToInt, ssa.ConvertIntToPtr: // nothing to do default: panic("unim") } return nil }
func (v Target) genLoad(a *allocator, instr *ssa.Load) { v.moveIntToReg(a, ssa.GetOperands(instr)[0], "r11") v.moveMemToMem("r11", "rbp", 0, -a.valOffset(instr), TypeStoreSizeInBits(instr.Type())/8) }
func (v Target) genStore(a *allocator, instr *ssa.Store) { ops := ssa.GetOperands(instr) v.moveIntToReg(a, ops[0], "r11") v.moveMemToMem("rbp", "r11", -a.valOffset(ops[1]), 0, TypeStoreSizeInBits(ops[1].Type())/8) }