optimized xor generation somewhat.

This commit is contained in:
Mattias Hansson 2026-01-02 14:10:53 +01:00
parent 25016018ac
commit 35e4b77ee3
2 changed files with 188 additions and 7 deletions

View file

@ -162,6 +162,26 @@ func (c *XorCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
}
}
// Normalize operands: leverage XOR commutativity to swap if needed
// This ensures word operands are in param1 when mixed with bytes
// Makes code generation simpler and more consistent
param1IsByteSized := false
if c.param1IsVar {
param1IsByteSized = (c.param1VarKind == compiler.KindByte)
} else {
param1IsByteSized = ((c.param1Value >> 8) & 0xFF) == 0
}
param2IsWord := c.param2IsVar && c.param2VarKind == compiler.KindWord
if param1IsByteSized && param2IsWord {
// Swap param1 and param2
c.param1VarName, c.param2VarName = c.param2VarName, c.param1VarName
c.param1VarKind, c.param2VarKind = c.param2VarKind, c.param1VarKind
c.param1Value, c.param2Value = c.param2Value, c.param1Value
c.param1IsVar, c.param2IsVar = c.param2IsVar, c.param1IsVar
}
return nil
}
@ -205,6 +225,22 @@ func (c *XorCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
// If destination is word, handle high byte
if c.destVarKind == compiler.KindWord {
// Optimization: skip high byte for self-assignment when param2 is byte-sized
// e.g., word_var = word_var ^ byte_var would just copy high byte to itself
if c.destVarName == c.param1VarName {
param2IsByteSized := false
if c.param2IsVar {
param2IsByteSized = (c.param2VarKind == compiler.KindByte)
} else {
param2IsByteSized = ((c.param2Value >> 8) & 0xFF) == 0
}
if param2IsByteSized {
// High byte would be: dest+1 = dest+1 ^ 0 (complete no-op)
return asm, nil
}
}
// Load high byte of param1
if c.param1IsVar {
if c.param1VarKind == compiler.KindWord {
@ -217,16 +253,18 @@ func (c *XorCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
}
// XOR with high byte of param2
// XOR with high byte of param2 (skip if param2 high byte is 0, as eor #0 is a no-op)
if c.param2IsVar {
if c.param2VarKind == compiler.KindWord {
asm = append(asm, fmt.Sprintf("\teor %s+1", c.param2VarName))
} else {
asm = append(asm, "\teor #0")
}
// Skip eor when param2 is byte var (high byte is 0)
} else {
hi := uint8((c.param2Value >> 8) & 0xFF)
asm = append(asm, fmt.Sprintf("\teor #$%02x", hi))
if hi != 0 {
asm = append(asm, fmt.Sprintf("\teor #$%02x", hi))
}
// Skip eor when param2 const high byte is 0
}
// Store high byte

View file

@ -71,7 +71,6 @@ func TestXorCommand_OldSyntax(t *testing.T) {
"\teor b",
"\tsta result",
"\tlda #0",
"\teor #0",
"\tsta result+1",
},
},
@ -183,7 +182,6 @@ func TestXorCommand_OldSyntax(t *testing.T) {
"\teor bval",
"\tsta result",
"\tlda wval+1",
"\teor #0",
"\tsta result+1",
},
},
@ -286,7 +284,6 @@ func TestXorCommand_NewSyntax(t *testing.T) {
"\teor b",
"\tsta result",
"\tlda #0",
"\teor #0",
"\tsta result+1",
},
},
@ -307,6 +304,152 @@ func TestXorCommand_NewSyntax(t *testing.T) {
"\tsta result+1",
},
},
{
name: "byte ^ byte_const -> word (optimization: skip eor #0)",
line: "result = b ^ 5",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
},
wantAsm: []string{
"\tlda b",
"\teor #$05",
"\tsta result",
"\tlda #0",
"\tsta result+1",
},
},
{
name: "byte ^ word -> word (swapped to word ^ byte)",
line: "result = bval ^ wval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("result", "", compiler.KindWord, 0)
},
wantAsm: []string{
"\tlda wval",
"\teor bval",
"\tsta result",
"\tlda wval+1",
"\tsta result+1",
},
},
{
name: "byte_const ^ byte -> word (optimization: skip eor #0)",
line: "result = 5 ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
},
wantAsm: []string{
"\tlda #$05",
"\teor b",
"\tsta result",
"\tlda #$00",
"\tsta result+1",
},
},
{
name: "byte ^ word_const -> word (no optimization)",
line: "result = b ^ 300",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
},
wantAsm: []string{
"\tlda b",
"\teor #$2c",
"\tsta result",
"\tlda #0",
"\teor #$01",
"\tsta result+1",
},
},
{
name: "word_const ^ byte -> word (optimization: skip eor #0)",
line: "result = 300 ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
},
wantAsm: []string{
"\tlda #$2c",
"\teor b",
"\tsta result",
"\tlda #$01",
"\tsta result+1",
},
},
{
name: "self-assignment: word ^= byte (optimization: skip high byte entirely)",
line: "wval = wval ^ bval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
},
wantAsm: []string{
"\tlda wval",
"\teor bval",
"\tsta wval",
},
},
{
name: "self-assignment reversed: word ^= byte (optimization via swap)",
line: "wval = bval ^ wval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
},
wantAsm: []string{
"\tlda wval",
"\teor bval",
"\tsta wval",
},
},
{
name: "self-assignment: word ^= byte_const (optimization: skip high byte entirely)",
line: "wval = wval ^ 42",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
},
wantAsm: []string{
"\tlda wval",
"\teor #$2a",
"\tsta wval",
},
},
{
name: "self-assignment: word ^= word_const (no optimization: high byte needed)",
line: "wval = wval ^ 300",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
},
wantAsm: []string{
"\tlda wval",
"\teor #$2c",
"\tsta wval",
"\tlda wval+1",
"\teor #$01",
"\tsta wval+1",
},
},
{
name: "self-assignment: word ^= word (no optimization: both high bytes needed)",
line: "wval = wval ^ wval2",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("wval2", "", compiler.KindWord, 0x5678)
},
wantAsm: []string{
"\tlda wval",
"\teor wval2",
"\tsta wval",
"\tlda wval+1",
"\teor wval2+1",
"\tsta wval+1",
},
},
{
name: "variable ^ literal",
line: "result = a ^ $AA",