Exemple #1
0
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
}
Exemple #2
0
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)
}
Exemple #3
0
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))
}
Exemple #4
0
func checkBr(instr *ssa.Br) error {
	ops := ssa.GetOperands(instr)
	if err := errIfNotLabelType(instr, ops[0].Type()); err != nil {
		return err
	}
	return nil
}
Exemple #5
0
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))

}
Exemple #6
0
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])
}
Exemple #7
0
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
}
Exemple #8
0
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")
}
Exemple #9
0
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")
	}
}
Exemple #10
0
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
}
Exemple #11
0
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
}
Exemple #12
0
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])
}
Exemple #13
0
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
}
Exemple #14
0
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
}
Exemple #15
0
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
}
Exemple #16
0
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))
}
Exemple #17
0
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
}
Exemple #18
0
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
}
Exemple #19
0
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
}
Exemple #20
0
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
}
Exemple #21
0
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")
	}
}
Exemple #22
0
// 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
}
Exemple #23
0
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)
}
Exemple #24
0
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)
}