Something went wrong. Try again.
🌱 A minimal programming language and compiler. git.urbach.dev/cli/q
high-performance programming-language compiler
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159package codegen
import ( "slices"
"git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/ssa" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types")
func (f *Function) executeBinaryOp(step *Step, instr *ssa.BinaryOp) { if step.Register == -1 && !instr.Op.IsComparison() { return }
left := f.ValueToStep[instr.Left] right := f.ValueToStep[instr.Right] live := slices.Concat(step.Live, []*Step{left, right}) source := f.resolveOperand(left, live) avoid := []cpu.Register{source}
if instr.Op == token.Div || instr.Op == token.Mod { avoid = append(avoid, f.CPU.DivisorRestricted...) }
operand := f.resolveOperand(right, live, avoid...) destination := step.Register isSpilled := f.isSpilled(destination)
if isSpilled { avoid = append(avoid, operand)
if instr.Op == token.Shl || instr.Op == token.Shr { avoid = append(avoid, f.CPU.ShiftRestricted...) }
destination = f.findTempRegister(live, avoid...) }
if instr.Op.IsComparison() { f.emitComparison(step, left, right, source, operand, destination, instr.Op) } else if isImmediate(right) { f.emitArithmeticImmediate(left, source, destination, right.Value.(*ssa.Int).Int, instr.Op) } else { f.emitArithmeticRegister(left, source, operand, destination, instr.Op) }
if isSpilled { f.storeSpill(step, destination) }}
// emitComparison emits a compare followed by a conditional set when the result register is needed.func (f *Function) emitComparison(step *Step, left *Step, right *Step, source cpu.Register, operand cpu.Register, destination cpu.Register, op token.Kind) { if isImmediate(right) { f.Assembler.Append(&asm.CompareNumber{Destination: source, Number: right.Value.(*ssa.Int).Int}) } else { f.Assembler.Append(&asm.Compare{Destination: source, Source: operand}) }
if step.Register != -1 { unsigned := types.IsUnsigned(left.Value.Type()) || types.IsUnsigned(right.Value.Type()) f.arch.conditionalSet(f, destination, op, unsigned) }}
// emitArithmeticImmediate emits an arithmetic instruction with an immediate right operand.func (f *Function) emitArithmeticImmediate(left *Step, source cpu.Register, destination cpu.Register, number int, op token.Kind) { switch op { case token.Add: f.Assembler.Append(&asm.AddNumber{Destination: destination, Source: source, Number: number}) case token.And, token.LogicalAnd: f.Assembler.Append(&asm.AndNumber{Destination: destination, Source: source, Number: number}) case token.Or, token.LogicalOr: f.Assembler.Append(&asm.OrNumber{Destination: destination, Source: source, Number: number}) case token.Sub: f.Assembler.Append(&asm.SubtractNumber{Destination: destination, Source: source, Number: number}) case token.Xor: f.Assembler.Append(&asm.XorNumber{Destination: destination, Source: source, Number: number}) case token.Shl: f.Assembler.Append(&asm.ShiftLeftNumber{Destination: destination, Source: source, Number: number}) case token.Shr: f.emitShiftRightImmediate(source, destination, number, left.Value.Type()) default: panic("not implemented: " + op.String()) }}
// emitArithmeticRegister emits an arithmetic instruction with a register right operand.func (f *Function) emitArithmeticRegister(left *Step, source cpu.Register, operand cpu.Register, destination cpu.Register, op token.Kind) { switch op { case token.Add: f.Assembler.Append(&asm.Add{Destination: destination, Source: source, Operand: operand}) case token.Sub: f.Assembler.Append(&asm.Subtract{Destination: destination, Source: source, Operand: operand}) case token.Mul: f.Assembler.Append(&asm.Multiply{Destination: destination, Source: source, Operand: operand}) case token.And, token.LogicalAnd: f.Assembler.Append(&asm.And{Destination: destination, Source: source, Operand: operand}) case token.Or, token.LogicalOr: f.Assembler.Append(&asm.Or{Destination: destination, Source: source, Operand: operand}) case token.Xor: f.Assembler.Append(&asm.Xor{Destination: destination, Source: source, Operand: operand}) case token.Shl: f.Assembler.Append(&asm.ShiftLeft{Destination: destination, Source: source, Operand: operand}) case token.Shr: f.emitShiftRightRegister(source, operand, destination, left.Value.Type()) case token.Div: f.emitDivision(source, operand, destination, left.Value.Type()) case token.Mod: f.emitModulo(source, operand, destination, left.Value.Type()) default: panic("not implemented: " + op.String()) }}
// emitShiftRightImmediate emits a shift-right instruction with an immediate count.func (f *Function) emitShiftRightImmediate(source cpu.Register, destination cpu.Register, count int, typ types.Type) { if types.IsUnsigned(typ) { f.Assembler.Append(&asm.ShiftRightNumber{Destination: destination, Source: source, Number: count}) } else { f.Assembler.Append(&asm.ShiftRightSignedNumber{Destination: destination, Source: source, Number: count}) }}
// emitShiftRightRegister emits a shift-right instruction with a register count.func (f *Function) emitShiftRightRegister(source cpu.Register, operand cpu.Register, destination cpu.Register, typ types.Type) { if types.IsUnsigned(typ) { f.Assembler.Append(&asm.ShiftRight{Destination: destination, Source: source, Operand: operand}) } else { f.Assembler.Append(&asm.ShiftRightSigned{Destination: destination, Source: source, Operand: operand}) }}
// emitDivision emits a division instruction.func (f *Function) emitDivision(source cpu.Register, operand cpu.Register, destination cpu.Register, typ types.Type) { if types.IsUnsigned(typ) { f.Assembler.Append(&asm.Divide{Destination: destination, Source: source, Operand: operand}) } else { f.Assembler.Append(&asm.DivideSigned{Destination: destination, Source: source, Operand: operand}) }}
// emitModulo emits a modulo instruction.func (f *Function) emitModulo(source cpu.Register, operand cpu.Register, destination cpu.Register, typ types.Type) { if types.IsUnsigned(typ) { f.Assembler.Append(&asm.Modulo{Destination: destination, Source: source, Operand: operand}) } else { f.Assembler.Append(&asm.ModuloSigned{Destination: destination, Source: source, Operand: operand}) }}
// isImmediate returns true if the operand is an integer with no assigned register.func isImmediate(operand *Step) bool { _, isInt := operand.Value.(*ssa.Int) return isInt && operand.Register == -1}