Fix IR peephole crash on lsigb.l + ext.b with mismatched register types

regression was introduced in commit 2ce7a63c1e
This commit is contained in:
Irmen de Jong committed 2026-10-03 14:01:56 +02:00
1 parent c9a7dc05d9
commit 8c2bc15b41
2 files changed
+54 -4

No files matched your search

@@ -1158,13 +1158,18 @@ jump p8_label_gen_2
mods.add(Mod(i+1, IRInstructions.binaryImmediate(Opcode.AND, IRDataType.LONG, outer.requireDest().registerNumber, 0xffff)))
mods.add(Mod(i, IRInstructions.move(IRDataType.LONG, outer.requireDest().registerNumber, inner.requireSrcA().registerNumber)))
}
// truncate-long/word-to-byte followed by zero-extend byte-to-word == mask with $ff
// truncate-long/word-to-byte followed by zero-extend byte-to-word == mask with $ff.
// This is only valid when the source and destination have the same type, because
// the rewritten loadr would otherwise access the source register with a different
// type than its definition (e.g. long source to word dest).
inner.opcode==Opcode.LSIGB && inner.type in setOf(IRDataType.WORD, IRDataType.LONG)
&& outer.opcode==Opcode.EXT && outer.type==IRDataType.BYTE
&& mid!=null && mid==outer.srcA?.register && inner.srcA!=null && outer.dest!=null
&& readCounts.getValue(mid)==1 -> {
mods.add(Mod(i+1, IRInstructions.binaryImmediate(Opcode.AND, inner.type!!, outer.requireDest().registerNumber, 0xff)))
mods.add(Mod(i, IRInstructions.move(inner.type!!, outer.requireDest().registerNumber, inner.requireSrcA().registerNumber)))
&& readCounts.getValue(mid)==1
&& inner.type == outer.requireDest().type -> {
val resultType = outer.requireDest().type
mods.add(Mod(i+1, IRInstructions.binaryImmediate(Opcode.AND, resultType, outer.requireDest().registerNumber, 0xff)))
mods.add(Mod(i, IRInstructions.move(resultType, outer.requireDest().registerNumber, inner.requireSrcA().registerNumber)))
}
else -> continue
}
@@ -514,6 +514,51 @@ class TestIRPeepholeOpt: FunSpec({
instr[2].requireImmediateInt() shouldBe 42
}
test("collapse lsigb.w + ext.b into loadr.w + and.w") {
val irProg = makeIRProgram(listOf(
IRInstructions.loadMemory(Opcode.LOADM, IRDataType.WORD, 7, IRMemory.direct("src")),
IRInstructions.binary(Opcode.LSIGB, IRDataType.WORD, 8, 7),
IRInstructions.binary(Opcode.EXT, IRDataType.BYTE, 9, 8),
IRInstructions.binary(Opcode.ADDR, IRDataType.WORD, 2, 9),
IRInstructions.returnVoid()
))
val opt = IRPeepholeOptimizer(irProg, false)
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.any { it.opcode == Opcode.LSIGB } shouldBe false
instr.any { it.opcode == Opcode.EXT && it.requireDest().register == VirtualRegister.int(9) } shouldBe false
instr.any {
it.opcode == Opcode.LOADR &&
it.type == IRDataType.WORD &&
it.requireDest().register == VirtualRegister.int(9) &&
it.requireSrcA().register == VirtualRegister.int(7)
} shouldBe true
instr.any {
it.opcode == Opcode.AND &&
it.type == IRDataType.WORD &&
it.requireDest().register == VirtualRegister.int(9) &&
it.requireImmediateInt() == 0xff
} shouldBe true
}
test("do not collapse lsigb.l + ext.b when source and dest types differ") {
// lsigb.l produces a byte from a long; ext.b widens it to word. Rewriting this as
// loadr + and would require accessing the long source as word, which conflicts with
// the register type system. The optimizer must leave the original sequence intact.
val irProg = makeIRProgram(listOf(
IRInstructions.loadMemory(Opcode.LOADM, IRDataType.LONG, 7, IRMemory.direct("src")),
IRInstructions.binary(Opcode.LSIGB, IRDataType.LONG, 8, 7),
IRInstructions.binary(Opcode.EXT, IRDataType.BYTE, 9, 8),
IRInstructions.binary(Opcode.ADDR, IRDataType.WORD, 2, 9),
IRInstructions.returnVoid()
))
val opt = IRPeepholeOptimizer(irProg, false)
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.any { it.opcode == Opcode.LSIGB } shouldBe true
instr.any { it.opcode == Opcode.EXT && it.requireDest().register == VirtualRegister.int(9) } shouldBe true
}
test("coalesce redundant LOADX/STOREX to same index is removed") {
val irProg = makeIRProgram(listOf(
IRInstructions.loadMemory(Opcode.LOADX, IRDataType.BYTE, 1, IRMemory.indexed("myArray", 10, indexRegType)),