Skip to content

Commit 1ad9f1c

Browse files
authored
Merge pull request #914 from boriel-basic/feat/unify_bool
Feat/unify bool
2 parents 8d22b6f + 45d5174 commit 1ad9f1c

91 files changed

Lines changed: 785 additions & 315 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/api/config.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import os
1313
import sys
1414
from collections.abc import Callable
15-
from enum import Enum
15+
from enum import StrEnum
1616

1717
from src.api import errmsg, global_, options, python_version_check
1818
from src.api.options import ANYTYPE, Action
@@ -24,14 +24,21 @@
2424
# Common setup and configuration for all tools
2525
# ------------------------------------------------------
2626
@enum.unique
27-
class ConfigSections(str, Enum):
27+
class ConfigSections(StrEnum):
2828
ZXBC = "zxbc"
2929
ZXBASM = "zxbasm"
3030
ZXBPP = "zxbpp"
3131

3232

3333
@enum.unique
34-
class OPTION(str, Enum):
34+
class OptimizationStrategy(StrEnum):
35+
Size = "size"
36+
Speed = "speed"
37+
Auto = "auto"
38+
39+
40+
@enum.unique
41+
class OPTION(StrEnum):
3542
OUTPUT_FILENAME = "output_filename"
3643
INPUT_FILENAME = "input_filename"
3744
STDERR_FILENAME = "stderr_filename"
@@ -76,6 +83,9 @@ class OPTION(str, Enum):
7683
ASM_ZXNEXT = "zxnext"
7784
FORCE_ASM_BRACKET = "force_asm_brackets"
7885

86+
# Optimization Preferences
87+
OPT_STRATEGY = "opt_strategy"
88+
7989

8090
OPTIONS = options.Options()
8191
OPTIONS_NOT_SAVED = {
@@ -226,6 +236,15 @@ def init() -> None:
226236
# Whether to show WXXX warning codes or not
227237
OPTIONS(Action.ADD, name=OPTION.HIDE_WARNING_CODES, type=bool, default=False, ignore_none=True)
228238

239+
# Optimization preferences
240+
OPTIONS(
241+
Action.ADD,
242+
name=OPTION.OPT_STRATEGY,
243+
type=OptimizationStrategy,
244+
default=OptimizationStrategy.Auto,
245+
ignore_none=True,
246+
)
247+
229248
OPTIONS(
230249
Action.ADD,
231250
name=OPTION.PROJECT_FILENAME,

src/api/errmsg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def warning_command_line_flag_deprecation(flag: str) -> None:
106106
"""Warning signaling command line flag is deprecated.
107107
This is a special warning that can't be silenced, and needs no line number nor filename.
108108
"""
109-
# msg_output(f"WARNING: deprecated flag {flag}") # TODO: To be enabled upon 1.18+
109+
msg_output(f"WARNING: deprecated flag {flag}") # TODO: To be enabled upon 1.18+
110110

111111

112112
# region [Warnings]

src/api/options.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
import enum
1111
import json
12-
from typing import Any
12+
from enum import StrEnum
13+
from typing import Any, Final
1314

1415
from src.api.exception import Error
1516

16-
__all__ = ["Option", "Options", "ANYTYPE", "Action"]
17+
__all__: Final[tuple[str, ...]] = "Option", "Options", "ANYTYPE", "Action"
1718

1819

1920
class ANYTYPE:
@@ -51,7 +52,7 @@ def __str__(self):
5152
return "Cannot pop option '%s'. Option stack is empty" % self.option
5253

5354

54-
class InvalidValueError(Error):
55+
class InvalidValueError(ValueError, Error):
5556
def __init__(self, option_name, _type, value):
5657
self.option = option_name
5758
self.value = value
@@ -159,7 +160,7 @@ def pop(self) -> Any:
159160
# Options commands
160161
# ----------------------------------------------------------------------
161162
@enum.unique
162-
class Action(str, enum.Enum):
163+
class Action(StrEnum):
163164
ADD = "add"
164165
ADD_IF_NOT_DEFINED = "add_if_not_defined"
165166
CLEAR = "clear"

src/api/symboltable/symboltable.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ def access_id(
360360
# The entry was already declared. If it's type is auto and the default type is not None,
361361
# update its type.
362362
if default_type is not None and result.type_ == self.basic_types[TYPE.unknown]:
363+
if default_type == self.basic_types[TYPE.boolean]:
364+
default_type = self.basic_types[TYPE.ubyte]
365+
363366
result.type_ = default_type
364367
warning_implicit_type(lineno, id_, default_type.name)
365368

src/arch/z80/backend/_8bit.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,14 +745,13 @@ def and8(cls, ins: Quad) -> list[str]:
745745
return output
746746

747747
output = cls.get_oper(op1, op2)
748-
# output.append('call __AND8')
748+
749749
lbl = tmp_label()
750750
output.append("or a")
751751
output.append("jr z, %s" % lbl)
752752
output.append("ld a, h")
753753
output.append("%s:" % lbl)
754754
output.append("push af")
755-
# REQUIRES.add('and8.asm')
756755

757756
return output
758757

src/arch/z80/backend/common.py

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Final
66

77
from src.api import global_, tmp_labels
8+
from src.api.config import OPTIONS, OptimizationStrategy
89
from src.api.exception import TempAlreadyFreedError
910

1011
from .runtime import LABEL_REQUIRED_MODULES, NAMESPACE, RUNTIME_LABELS
@@ -296,12 +297,52 @@ def get_bytes_size(elements: list[str]) -> int:
296297
return len(get_bytes(elements))
297298

298299

300+
def to_bool(stype: DataType) -> list[str]:
301+
"""Returns the instruction sequence for converting the number given number (in the stack)
302+
to boolean (just 0 (False) or non-zero (True))."""
303+
304+
if stype in (U8_t, I8_t):
305+
return []
306+
307+
if stype in (U16_t, I16_t):
308+
return [
309+
"ld a, h" "or l",
310+
]
311+
312+
if stype in (U32_t, I32_t, F16_t):
313+
return ["ld a, h" "or l" "or d", "or e,"]
314+
315+
if stype == F_t:
316+
return [
317+
"or b",
318+
"or c",
319+
"or d",
320+
"or e",
321+
]
322+
323+
raise NotImplementedError(f"type conversion from {stype} to bool is undefined")
324+
325+
326+
def normalize_boolean() -> list[str]:
327+
if OPTIONS.opt_strategy == OptimizationStrategy.Size:
328+
return [runtime_call(RuntimeLabel.NORMALIZE_BOOLEAN)]
329+
330+
return [
331+
"sub 1", # Carry if A = 0
332+
"sbc a, a", # 0xFF if A was 0, 0 otherwise
333+
"inc a", # 0 if A was 0, 1 otherwise
334+
]
335+
336+
299337
def to_byte(stype: DataType) -> list[str]:
300338
"""Returns the instruction sequence for converting from
301339
the given type to byte.
302340
"""
303341
output = []
304342

343+
if stype == BOOL_t:
344+
return normalize_boolean()
345+
305346
if stype in (I8_t, U8_t):
306347
return []
307348

@@ -322,7 +363,10 @@ def to_word(stype: DataType) -> list[str]:
322363
"""
323364
output = [] # List of instructions
324365

325-
if stype == U8_t: # Byte to word
366+
if stype == BOOL_t:
367+
output.extend(normalize_boolean())
368+
369+
if stype in (BOOL_t, U8_t): # Byte to word
326370
output.append("ld l, a")
327371
output.append("ld h, 0")
328372

@@ -347,25 +391,39 @@ def to_long(stype: DataType) -> list[str]:
347391
"""
348392
output = [] # List of instructions
349393

394+
if stype == BOOL_t:
395+
output = normalize_boolean()
396+
output.extend(
397+
[
398+
"ld l, a",
399+
"ld h, 0",
400+
"ld e, h",
401+
"ld d, h",
402+
]
403+
)
404+
return output
405+
350406
if stype in {I8_t, U8_t, F16_t}: # Byte to word
351407
output = to_word(stype)
352408

353409
if stype != F16_t: # If it's a byte, just copy H to D,E
354410
output.append("ld e, h")
355411
output.append("ld d, h")
356412

357-
if stype in (I16_t, F16_t): # Signed byte or fixed to word
413+
elif stype in (I16_t, F16_t): # Signed byte or fixed to word
358414
output.append("ld a, h")
359415
output.append("add a, a")
360416
output.append("sbc a, a")
361417
output.append("ld e, a")
362418
output.append("ld d, a")
363419

364-
elif stype == "u16":
420+
elif stype == U16_t:
365421
output.append("ld de, 0")
366422

367423
elif stype == F_t:
368424
output.append(runtime_call(RuntimeLabel.FTOU32REG))
425+
else:
426+
raise NotImplementedError(f"type conversion from {stype} to long is undefined")
369427

370428
return output
371429

@@ -374,16 +432,30 @@ def to_fixed(stype: DataType) -> list[str]:
374432
"""Returns the instruction sequence for converting the given
375433
type stored in DE,HL to fixed DE,HL.
376434
"""
377-
output = [] # List of instructions
435+
if stype == BOOL_t:
436+
output = to_word(stype)
437+
output.extend(
438+
[
439+
"ex de, hl",
440+
"ld hl, 0", # 'Truncate' the fixed point
441+
]
442+
)
443+
return output
378444

379445
if is_int_type(stype):
380446
output = to_word(stype)
381-
output.append("ex de, hl")
382-
output.append("ld hl, 0") # 'Truncate' the fixed point
383-
elif stype == F_t:
384-
output.append(runtime_call(RuntimeLabel.FTOF16REG))
447+
output.extend(
448+
[
449+
"ex de, hl",
450+
"ld hl, 0", # 'Truncate' the fixed point
451+
]
452+
)
453+
return output
385454

386-
return output
455+
if stype == F_t:
456+
return [runtime_call(RuntimeLabel.FTOF16REG)]
457+
458+
raise NotImplementedError(f"type conversion from {stype} to fixed")
387459

388460

389461
def to_float(stype: DataType) -> list[str]:
@@ -399,17 +471,22 @@ def to_float(stype: DataType) -> list[str]:
399471
output.append(runtime_call(RuntimeLabel.F16TOFREG))
400472
return output
401473

474+
if stype == BOOL_t:
475+
output.extend(normalize_boolean())
476+
402477
# If we reach this point, it's an integer type
403-
if stype == U8_t:
478+
if stype in (BOOL_t, U8_t): # The ZX Spectrum ROM FP-Calc already returns 0 or 1 for Booleans
404479
output.append(runtime_call(RuntimeLabel.U8TOFREG))
405480
elif stype == I8_t:
406481
output.append(runtime_call(RuntimeLabel.I8TOFREG))
407-
else:
482+
elif stype in {I16_t, I32_t, U16_t, U32_t}:
408483
output = to_long(stype)
409484
if stype in (I16_t, I32_t):
410485
output.append(runtime_call(RuntimeLabel.I32TOFREG))
411486
else:
412487
output.append(runtime_call(RuntimeLabel.U32TOFREG))
488+
else:
489+
raise NotImplementedError(f"type conversion from {stype} to float is undefined")
413490

414491
return output
415492

src/arch/z80/backend/generic.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
get_bytes_size,
2525
new_ASMID,
2626
runtime_call,
27+
to_bool,
2728
to_byte,
2829
to_fixed,
2930
to_float,
@@ -341,7 +342,7 @@ def _cast(ins: Quad):
341342
xsB = sB = YY_TYPES[tB] # Type sizes
342343

343344
output = []
344-
if tA in ("u8", "i8"):
345+
if tA in ("u8", "i8", "bool"):
345346
output.extend(Bits8.get_oper(ins[4]))
346347
elif tA in ("u16", "i16"):
347348
output.extend(Bits16.get_oper(ins[4]))
@@ -364,6 +365,10 @@ def _cast(ins: Quad):
364365
output.extend(to_fixed(tA))
365366
elif tB == "f":
366367
output.extend(to_float(tA))
368+
elif tB == "bool":
369+
output.extend(to_bool(tA))
370+
else:
371+
raise exception.GenericError("Internal error: invalid typecast from %s to %s" % (tA, tB))
367372

368373
xsB += sB % 2 # make it even (round up)
369374

src/arch/z80/backend/main.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@
100100
# String comparison functions
101101
# String arithmetic functions
102102
from ._str import String
103-
from .common import runtime_call
104103
from .generic import (
105104
_call,
106105
_cast,
@@ -130,7 +129,6 @@
130129
from .icinstruction import ICInstruction
131130
from .quad import Quad
132131
from .runtime import NAMESPACE
133-
from .runtime import Labels as RuntimeLabel
134132

135133
__all__ = ("Backend",)
136134

@@ -677,16 +675,6 @@ def emit_prologue() -> list[str]:
677675

678676
return output
679677

680-
@staticmethod
681-
def emit_cast_to_bool():
682-
"""Convert a byte value to boolean (0 or 1) if
683-
the global flag strictBool is True
684-
"""
685-
if not OPTIONS.strict_bool:
686-
return []
687-
688-
return ["pop af", runtime_call(RuntimeLabel.NORMALIZE_BOOLEAN), "push af"]
689-
690678
@staticmethod
691679
def emit_epilogue() -> list[str]:
692680
"""This special ending autoinitializes required inits
@@ -780,9 +768,6 @@ def emit(self, *, optimize: bool = True) -> list[str]:
780768
output: list[str] = []
781769
for quad in self.MEMORY:
782770
self._output_join(output, self._QUAD_TABLE[quad.instr].func(quad), optimize=optimize)
783-
# If it is a boolean operation convert it to 0/1 if the STRICT_BOOL flag is True
784-
if common.RE_BOOL.match(quad.instr):
785-
self._output_join(output, self.emit_cast_to_bool(), optimize=optimize)
786771

787772
if optimize and OPTIONS.optimization_level > 1:
788773
self.remove_unused_labels(output)

0 commit comments

Comments
 (0)