Skip to content

Commit

Permalink
Merge pull request #942 from boriel-basic/fix/shr_and_shl_bool
Browse files Browse the repository at this point in the history
fix: using SHL or SHR with boolean expressions
  • Loading branch information
boriel authored Dec 31, 2024
2 parents 9e08bd0 + 0b8dc9f commit b09729d
Show file tree
Hide file tree
Showing 19 changed files with 168 additions and 67 deletions.
65 changes: 31 additions & 34 deletions src/arch/z80/visitor/translator_inst_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,31 @@ def ic_aaddr(self, t1, t2) -> None:
self.emit("aaddr", t1, t2)

def ic_abs(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
self.emit("abs" + self.TSUFFIX(type_), t1, t2)
self.emit(f"abs{self.TSUFFIX(type_)}", t1, t2)

def ic_add(self, type_: TYPE | sym.BASICTYPE, t1, t2, t3) -> None:
self.emit("add" + self.TSUFFIX(type_), t1, t2, t3)
self.emit(f"add{self.TSUFFIX(type_)}", t1, t2, t3)

def ic_aload(self, type_: TYPE | sym.BASICTYPE, t1, mangle: str) -> None:
self.emit("aload" + self.TSUFFIX(type_), t1, mangle)
self.emit(f"aload{self.TSUFFIX(type_)}", t1, mangle)

def ic_and(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit(f"and{self._no_bool(type_)}", t, t1, t2)

def ic_astore(self, type_: TYPE | sym.BASICTYPE, addr: str, t) -> None:
self.emit("astore" + self.TSUFFIX(type_), addr, t)
self.emit(f"astore{self.TSUFFIX(type_)}", addr, t)

def ic_band(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("band" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"band{self.TSUFFIX(type_)}", t, t1, t2)

def ic_bnot(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
self.emit("bnot" + self.TSUFFIX(type_), t1, t2)
self.emit(f"bnot{self.TSUFFIX(type_)}", t1, t2)

def ic_bor(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("bor" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"bor{self.TSUFFIX(type_)}", t, t1, t2)

def ic_bxor(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("bxor" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"bxor{self.TSUFFIX(type_)}", t, t1, t2)

def ic_call(self, label: str, num: int) -> None:
self.emit("call", label, num)
Expand All @@ -91,7 +91,7 @@ def ic_deflabel(self, label: str, t) -> None:
self.emit("deflabel", label, t)

def ic_div(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("div" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"div{self.TSUFFIX(type_)}", t, t1, t2)

def ic_end(self, t) -> None:
self.emit("end", t)
Expand All @@ -100,7 +100,7 @@ def ic_enter(self, arg) -> None:
self.emit("enter", arg)

def ic_eq(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("eq" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"eq{self.TSUFFIX(type_)}", t, t1, t2)

def ic_exchg(self) -> None:
self.emit("exchg")
Expand All @@ -109,13 +109,13 @@ def ic_fparam(self, type_: TYPE | sym.BASICTYPE, t) -> None:
self.emit(f"fparam{self._no_bool(type_)}", t)

def ic_fpload(self, type_: TYPE | sym.BASICTYPE, t, offset) -> None:
self.emit("fpload" + self.TSUFFIX(type_), t, offset)
self.emit(f"fpload{self.TSUFFIX(type_)}", t, offset)

def ic_ge(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("ge" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"ge{self.TSUFFIX(type_)}", t, t1, t2)

def ic_gt(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("gt" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"gt{self.TSUFFIX(type_)}", t, t1, t2)

def ic_in(self, t) -> None:
self.emit("in", t)
Expand All @@ -142,7 +142,7 @@ def ic_larrd(self, offset, arg1, size, arg2, bound_ptrs) -> None:
self.emit("larrd", offset, arg1, size, arg2, bound_ptrs)

def ic_le(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("le" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"le{self.TSUFFIX(type_)}", t, t1, t2)

def ic_leave(self, convention: str) -> None:
self.emit("leave", convention)
Expand All @@ -151,10 +151,10 @@ def ic_lenstr(self, t1, t2) -> None:
self.emit("lenstr", t1, t2)

def ic_load(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
self.emit("load" + self.TSUFFIX(type_), t1, t2)
self.emit(f"load{self.TSUFFIX(type_)}", t1, t2)

def ic_lt(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("lt" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"lt{self.TSUFFIX(type_)}", t, t1, t2)

def ic_lvard(self, offset, default_value: list) -> None:
self.emit("lvard", offset, default_value)
Expand All @@ -166,16 +166,16 @@ def ic_memcopy(self, t1, t2, t3) -> None:
self.emit("memcopy", t1, t2, t3)

def ic_mod(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("mod" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"mod{self.TSUFFIX(type_)}", t, t1, t2)

def ic_mul(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("mul" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"mul{self.TSUFFIX(type_)}", t, t1, t2)

def ic_ne(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("ne" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"ne{self.TSUFFIX(type_)}", t, t1, t2)

def ic_neg(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
self.emit("neg" + self.TSUFFIX(type_), t1, t2)
self.emit(f"neg{self.TSUFFIX(type_)}", t1, t2)

def ic_nop(self) -> None:
self.emit("nop")
Expand All @@ -186,9 +186,6 @@ def ic_not(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
def ic_or(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit(f"or{self._no_bool(type_)}", t, t1, t2)

def ic_org(self, type_: TYPE | sym.BASICTYPE) -> None:
self.emit("org" + self.TSUFFIX(type_))

def ic_out(self, t1, t2) -> None:
self.emit("out", t1, t2)

Expand All @@ -199,40 +196,40 @@ def ic_paddr(self, t1, t2) -> None:
self.emit("paddr", t1, t2)

def ic_paload(self, type_: TYPE | sym.BASICTYPE, t, offset: int) -> None:
self.emit("paload" + self.TSUFFIX(type_), t, offset)
self.emit(f"paload{self.TSUFFIX(type_)}", t, offset)

def ic_param(self, type_: TYPE | sym.BASICTYPE, t) -> None:
self.emit("param" + self.TSUFFIX(type_), t)
self.emit(f"param{self.TSUFFIX(type_)}", t)

def ic_pastore(self, type_: TYPE | sym.BASICTYPE, offset, t) -> None:
self.emit("pastore" + self.TSUFFIX(type_), offset, t)
self.emit(f"pastore{self.TSUFFIX(type_)}", offset, t)

def ic_pload(self, type_: TYPE | sym.BASICTYPE, t1, offset) -> None:
self.emit("pload" + self.TSUFFIX(type_), t1, offset)
self.emit(f"pload{self.TSUFFIX(type_)}", t1, offset)

def ic_pow(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("pow" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"pow{self.TSUFFIX(type_)}", t, t1, t2)

def ic_pstore(self, type_: TYPE | sym.BASICTYPE, offset, t) -> None:
self.emit("pstore" + self.TSUFFIX(type_), offset, t)
self.emit(f"pstore{self.TSUFFIX(type_)}", offset, t)

def ic_ret(self, type_: TYPE | sym.BASICTYPE, t, addr) -> None:
self.emit("ret" + self.TSUFFIX(type_), t, addr)
self.emit(f"ret{self.TSUFFIX(type_)}", t, addr)

def ic_return(self, addr) -> None:
self.emit("ret", addr)

def ic_shl(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit(f"shl{self._no_bool(type_)}", t, t1, t2)
self.emit(f"shl{self.TSUFFIX(type_)}", t, t1, t2)

def ic_shr(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit(f"shr{self._no_bool(type_)}", t, t1, t2)
self.emit(f"shr{self.TSUFFIX(type_)}", t, t1, t2)

def ic_store(self, type_: TYPE | sym.BASICTYPE, t1, t2) -> None:
self.emit("store" + self.TSUFFIX(type_), t1, t2)
self.emit(f"store{self.TSUFFIX(type_)}", t1, t2)

def ic_sub(self, type_: TYPE | sym.BASICTYPE, t, t1, t2) -> None:
self.emit("sub" + self.TSUFFIX(type_), t, t1, t2)
self.emit(f"sub{self.TSUFFIX(type_)}", t, t1, t2)

def ic_var(self, name: str, size_) -> None:
self.emit("var", name, size_)
Expand Down
47 changes: 27 additions & 20 deletions src/symbols/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ def make_node(cls, operator, left, right, lineno, func=None, type_=None):
return None

a, b = left, right # short form names
if operator in {
"BAND",
"BOR",
"BXOR",
"AND",
"OR",
"XOR",
"MINUS",
"MULT",
"DIV",
"SHL",
"SHR",
} and not check.is_numeric(a, b):
errmsg.error(lineno, f"Operator {operator} cannot be used with strings")
return None

if operator not in {"AND", "OR", "XOR"}:
# Non-boolean operators use always numeric operands.
# We ensure operands are correctly converted to 0|1 values if they're boolean
if a.type_ == TYPE.boolean:
a = SymbolTYPECAST.make_node(TYPE.ubyte, a, lineno)

if b.type_ == TYPE.boolean:
b = SymbolTYPECAST.make_node(TYPE.ubyte, b, lineno)

# Check for constant non-numeric operations
c_type = check.common_type(a, b) # Resulting operation type or None
if TYPE.is_numeric(c_type): # there must be a common type for a and b
Expand All @@ -90,31 +115,13 @@ def make_node(cls, operator, left, right, lineno, func=None, type_=None):
b = SymbolTYPECAST.make_node(c_type, b, lineno) # ensure type
return SymbolCONSTEXPR(cls(operator, a, b, lineno, type_=type_, func=func), lineno=lineno)

if operator in {
"BNOT",
"BAND",
"BOR",
"BXOR",
"NOT",
"AND",
"OR",
"XOR",
"MINUS",
"MULT",
"DIV",
"SHL",
"SHR",
} and not check.is_numeric(a, b):
errmsg.error(lineno, "Operator %s cannot be used with STRINGS" % operator)
return None

if check.is_string(a, b) and func is not None: # Are they STRING Constants?
if operator == "PLUS":
return SymbolSTRING(func(a.value, b.value), lineno)

return SymbolNUMBER(int(func(a.text, b.text)), type_=TYPE.ubyte, lineno=lineno) # Convert to u8 (boolean)

if operator in ("BNOT", "BAND", "BOR", "BXOR"):
if operator in {"BNOT", "BAND", "BOR", "BXOR"}:
if TYPE.is_decimal(c_type):
c_type = TYPE.long_

Expand All @@ -129,7 +136,7 @@ def make_node(cls, operator, left, right, lineno, func=None, type_=None):
return None

if type_ is None:
if operator in ("LT", "GT", "EQ", "LE", "GE", "NE", "AND", "OR", "XOR", "NOT"):
if operator in {"LT", "GT", "EQ", "LE", "GE", "NE", "AND", "OR", "XOR"}:
type_ = TYPE.boolean
else:
type_ = c_type
Expand Down
17 changes: 17 additions & 0 deletions tests/functional/arch/zx48k/add8.asm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ _b:
ld a, (_a)
add a, h
ld (_b), a
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
push af
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
ld h, a
pop af
add a, h
ld (_b), a
ld hl, 0
ld b, h
ld c, l
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/arch/zx48k/add8.bas
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ b = a + 1
b = 0 + a
b = 1 + a
b = a + a
b = (a = a) + (a = a)

21 changes: 19 additions & 2 deletions tests/functional/arch/zx48k/divu8.asm
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ _b:
ld hl, (_a - 1)
call .core.__DIVU8_FAST
ld (_b), a
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
push af
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
ld h, a
pop af
call .core.__DIVU8_FAST
ld (_b), a
ld hl, 0
ld b, h
ld c, l
Expand All @@ -66,7 +83,7 @@ _b:
ei
ret
;; --- end of user code ---
#line 1 "/zxbasic/src/arch/zx48k/library-asm/div8.asm"
#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm"
; --------------------------------
push namespace core
__DIVU8: ; 8 bit unsigned integer division
Expand Down Expand Up @@ -131,5 +148,5 @@ __MODI8_FAST: ; __FASTCALL__ entry
ld a, l ; remainder
ret ; a = Modulus
pop namespace
#line 44 "divu8.bas"
#line 61 "arch/zx48k/divu8.bas"
END
2 changes: 2 additions & 0 deletions tests/functional/arch/zx48k/divu8.bas
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ b = 1 / a
b = 2 / a
b = 4 / a
b = a / a
b = (a = a) / (a = a)

21 changes: 19 additions & 2 deletions tests/functional/arch/zx48k/mul8.asm
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ _b:
ld a, (_a)
call .core.__MUL8_FAST
ld (_b), a
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
push af
ld hl, (_a - 1)
ld a, (_a)
sub h
sub 1
sbc a, a
neg
ld h, a
pop af
call .core.__MUL8_FAST
ld (_b), a
ld hl, 0
ld b, h
ld c, l
Expand All @@ -67,7 +84,7 @@ _b:
ei
ret
;; --- end of user code ---
#line 1 "/zxbasic/src/arch/zx48k/library-asm/mul8.asm"
#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul8.asm"
push namespace core
__MUL8: ; Performs 8bit x 8bit multiplication
PROC
Expand Down Expand Up @@ -113,5 +130,5 @@ __MUL8B:
ret ; result = HL
ENDP
pop namespace
#line 45 "mul8.bas"
#line 62 "arch/zx48k/mul8.bas"
END
2 changes: 2 additions & 0 deletions tests/functional/arch/zx48k/mul8.bas
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ b = 1 * a
b = 2 * a
b = 4 * a
b = a * a
b = (a = a) * (a = a)

5 changes: 4 additions & 1 deletion tests/functional/arch/zx48k/shlu16.asm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ _b:
ld de, (_a)
ld hl, (_a)
call .core.__EQ16
sub 1
sbc a, a
inc a
push af
ld a, (_b)
pop hl
Expand Down Expand Up @@ -100,5 +103,5 @@ __EQ16: ; Test if 16bit values HL == DE
inc a
ret
pop namespace
#line 68 "arch/zx48k/shlu16.bas"
#line 71 "arch/zx48k/shlu16.bas"
END
Loading

0 comments on commit b09729d

Please sign in to comment.