From 56915fb3231b36789ed0f291a5c112b8d4a82593 Mon Sep 17 00:00:00 2001 From: Alastair Reid Date: Tue, 21 Apr 2026 18:09:53 +0100 Subject: [PATCH 1/2] Literal hex numbers: treat these as bitvectors not ints --- libISA/isa_lexer.mll | 9 ++++++++- tests/backends/info_00.isa | 16 ++++++++-------- tests/backends/int_print_dec.isa | 4 ++-- tests/backends/int_print_hex.isa | 16 ++++++++-------- tests/backends/sintN_cvt_from_int_00.isa | 8 ++++---- tests/backends/stmt_case_04.isa | 2 +- tests/lit/tcheck/funcall06.isa | 2 +- tests/test_cases_backend.ml | 6 +----- tests/xform_constprop_test.ml | 4 ++-- 9 files changed, 35 insertions(+), 32 deletions(-) diff --git a/libISA/isa_lexer.mll b/libISA/isa_lexer.mll index 76e29d74..1422277c 100644 --- a/libISA/isa_lexer.mll +++ b/libISA/isa_lexer.mll @@ -125,7 +125,14 @@ rule token = parse { BITSLIT(Primops.mkBits (int_of_string len) (Z.of_string_base 10 digits)) } | (['0'-'9']+ as len) '\'' 'x' (['0'-'9' 'A'-'F' 'a'-'f' '_']+ as nibbles) (* deprecated *) { BITSLIT(Primops.mkBits (int_of_string len) (Z.of_string_base 16 nibbles)) } - | '0''x'(['0'-'9' 'A'-'F' 'a'-'f' '_']+ as nibbles) { INTLIT(None, Z.of_string_base 16 nibbles) } (* todo: change to BITLIT *) + | '0''x'(['0'-'9' 'A'-'F' 'a'-'f' '_']+ as nibbles) + { + if true then + let x = Utils.drop_chars nibbles '_' in + BITSLIT(Primops.mkBits (4 * String.length x) (Z.of_string_base 16 x)) + else + INTLIT(None, Z.of_string_base 16 nibbles) + } | 'i' (['0'-'9']+ as len) '\'' 'b' (['0'-'1']+ as bits) { INTLIT(Some (int_of_string len), Z.of_string_base 2 bits) } | 'i' (['0'-'9']+ as len) '\'' 'd' (['0'-'9']+ as digits) { INTLIT(Some (int_of_string len), Z.of_string digits) } | 'i' (['0'-'9']+ as len) '\'' 'x' (['0'-'9' 'A'-'F' 'a'-'f']+ as nibbles) { INTLIT(Some (int_of_string len), Z.of_string_base 16 nibbles) } diff --git a/tests/backends/info_00.isa b/tests/backends/info_00.isa index df8ab898..03d764c1 100644 --- a/tests/backends/info_00.isa +++ b/tests/backends/info_00.isa @@ -33,15 +33,15 @@ begin // Some runtimes optimize the handling of 64-bit values // so test the extreme values and near neighbors. - let max_64 := 0x7fff_ffff_ffff_ffff; - let min_64 := -0x8000_0000_0000_0000; + let max_64 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff); + let min_64 := -Std::Bits::Unsigned(0x8000_0000_0000_0000); Std::Info(1, "{max_64}"); // CHECK: {{^9223372036854775807$}} Std::Info(1, "{min_64}"); // CHECK: {{^-9223372036854775808$}} - let max_64m1 := 0x7fff_ffff_ffff_fffe; - let min_64p1 := -0x7fff_ffff_ffff_ffff; + let max_64m1 := Std::Bits::Unsigned(0x7fff_ffff_ffff_fffe); + let min_64p1 := -Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff); Std::Info(1, "{max_64m1}"); // CHECK: {{^9223372036854775806$}} Std::Info(1, "{min_64p1}"); @@ -49,8 +49,8 @@ begin // Most runtimes use 128 bits as the default int sizes // so test the extreme values and near neighbors. - let max_128 := 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff; - let min_128 := -0x8000_0000_0000_0000_0000_0000_0000_0000; + let max_128 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); + let min_128 := -Std::Bits::Unsigned(0x8000_0000_0000_0000_0000_0000_0000_0000); Std::Info(1, "{max_128}"); // CHECK-c23: {{^0x7fffffffffffffffffffffffffffffff$}} // CHECK-fallback: {{^0x7fffffffffffffffffffffffffffffff$}} @@ -60,8 +60,8 @@ begin // CHECK-fallback: {{^-0x80000000000000000000000000000000$}} // CHECK-interpreter: {{^-170141183460469231731687303715884105728$}} - let max_128m1 := 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe; - let min_128p1 := -0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff; + let max_128m1 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe); + let min_128p1 := -Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); Std::Info(1, "{max_128m1}"); // CHECK-c23: {{^0x7ffffffffffffffffffffffffffffffe$}} // CHECK-fallback: {{^0x7ffffffffffffffffffffffffffffffe$}} diff --git a/tests/backends/int_print_dec.isa b/tests/backends/int_print_dec.isa index d5a03331..8d0d3d87 100644 --- a/tests/backends/int_print_dec.isa +++ b/tests/backends/int_print_dec.isa @@ -8,7 +8,7 @@ begin Std::Print::Integer::Dec(-42); Print("\n"); // CHECK: {{^-42$}} - let max_128 := 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff; + let max_128 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); Std::Print::Integer::Dec(max_128); Print("\n"); // CHECK-ac: {{^0x7fffffffffffffffffffffffffffffff$}} // CHECK-c23: {{^0x7fffffffffffffffffffffffffffffff$}} @@ -16,7 +16,7 @@ begin // CHECK-interpreter: {{^170141183460469231731687303715884105727$}} // CHECK-sc: {{^0x7fffffffffffffffffffffffffffffff$}} - let min_128 := -0x8000_0000_0000_0000_0000_0000_0000_0000; + let min_128 := -Std::Bits::Unsigned(0x8000_0000_0000_0000_0000_0000_0000_0000); Std::Print::Integer::Dec(min_128); Print("\n"); // CHECK-ac: {{^-0x80000000000000000000000000000000$}} // CHECK-c23: {{^-0x80000000000000000000000000000000$}} diff --git a/tests/backends/int_print_hex.isa b/tests/backends/int_print_hex.isa index 3eac9103..d082bab2 100644 --- a/tests/backends/int_print_hex.isa +++ b/tests/backends/int_print_hex.isa @@ -10,15 +10,15 @@ begin // Some runtimes optimize the handling of 64-bit values // so test the extreme values and near neighbors. - let max_64 := 0x7fff_ffff_ffff_ffff; - let min_64 := -0x8000_0000_0000_0000; + let max_64 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff); + let min_64 := -Std::Bits::Unsigned(0x8000_0000_0000_0000); Std::Print::Integer::Hex(max_64); Print("\n"); // CHECK: {{^0x7fffffffffffffff$}} Std::Print::Integer::Hex(min_64); Print("\n"); // CHECK: {{^-0x8000000000000000$}} - let max_64m1 := 0x7fff_ffff_ffff_fffe; - let min_64p1 := -0x7fff_ffff_ffff_ffff; + let max_64m1 := Std::Bits::Unsigned(0x7fff_ffff_ffff_fffe); + let min_64p1 := -Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff); Std::Print::Integer::Hex(max_64m1); Print("\n"); // CHECK: {{^0x7ffffffffffffffe$}} Std::Print::Integer::Hex(min_64p1); Print("\n"); @@ -26,15 +26,15 @@ begin // Most runtimes use 128 bits as the default int sizes // so test the extreme values and near neighbors. - let max_128 := 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff; - let min_128 := -0x8000_0000_0000_0000_0000_0000_0000_0000; + let max_128 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); + let min_128 := -Std::Bits::Unsigned(0x8000_0000_0000_0000_0000_0000_0000_0000); Std::Print::Integer::Hex(max_128); Print("\n"); // CHECK: {{^0x7fffffffffffffffffffffffffffffff$}} Std::Print::Integer::Hex(min_128); Print("\n"); // CHECK: {{^-0x80000000000000000000000000000000$}} - let max_128m1 := 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe; - let min_128p1 := -0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff; + let max_128m1 := Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_fffe); + let min_128p1 := -Std::Bits::Unsigned(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); Std::Print::Integer::Hex(max_128m1); Print("\n"); // CHECK: {{^0x7ffffffffffffffffffffffffffffffe$}} Std::Print::Integer::Hex(min_128p1); Print("\n"); diff --git a/tests/backends/sintN_cvt_from_int_00.isa b/tests/backends/sintN_cvt_from_int_00.isa index 5c175c09..b7e1a45a 100644 --- a/tests/backends/sintN_cvt_from_int_00.isa +++ b/tests/backends/sintN_cvt_from_int_00.isa @@ -8,13 +8,13 @@ end function main() -> Builtin::Foreign::CInt begin - Std::Print::SInt::Hex(FUT_8(0x0)); Print("\n"); + Std::Print::SInt::Hex(FUT_8(0)); Print("\n"); // CHECK: i8'x0 - Std::Print::SInt::Hex(FUT_8(0x7f)); Print("\n"); + Std::Print::SInt::Hex(FUT_8(127)); Print("\n"); // CHECK: i8'x7f - Std::Print::SInt::Hex(FUT_8(-0x1)); Print("\n"); + Std::Print::SInt::Hex(FUT_8(-1)); Print("\n"); // CHECK: -i8'x1 - Std::Print::SInt::Hex(FUT_8(-0x80)); Print("\n"); + Std::Print::SInt::Hex(FUT_8(-128)); Print("\n"); // CHECK: -i8'x80 return Builtin::Foreign::CInt::From_Integer(0); diff --git a/tests/backends/stmt_case_04.isa b/tests/backends/stmt_case_04.isa index b9cccbb8..57695226 100644 --- a/tests/backends/stmt_case_04.isa +++ b/tests/backends/stmt_case_04.isa @@ -6,7 +6,7 @@ function Test(x : Integer) -> Integer begin case x of - when 0x1_0000_0000_0000_0000 => return 1; + when 18_446_744_073_709_551_616 => return 1; // 2^64 // CHECK: Unimplemented large (> 64 bit) integer pattern otherwise => return 10; end; diff --git a/tests/lit/tcheck/funcall06.isa b/tests/lit/tcheck/funcall06.isa index d3a147eb..8ae944b3 100644 --- a/tests/lit/tcheck/funcall06.isa +++ b/tests/lit/tcheck/funcall06.isa @@ -3,7 +3,7 @@ function F(x : Integer, y : Integer := 1, z : Integer := 2) -> Integer begin - return x * 0x100 + y * 0x10 + z; + return x * 256 + y * 16 + z; end function main() -> Builtin::Foreign::CInt diff --git a/tests/test_cases_backend.ml b/tests/test_cases_backend.ml index 830a3cdd..c7a12e92 100644 --- a/tests/test_cases_backend.ml +++ b/tests/test_cases_backend.ml @@ -48,11 +48,7 @@ let expr : test_case list = ( "literal hex", [ Backend_C; Backend_Verilog ], - "function F() -> Integer begin return 0x01_0; end" ); - - ( "literal hex (negative)", - [ Backend_C; Backend_Verilog ], - "function F() -> Integer begin return -0x01_0; end" ); + "function F() -> Bits(12) begin return 0x01_0; end" ); ( "literal bitvector", [ Backend_C; Backend_Verilog ], diff --git a/tests/xform_constprop_test.ml b/tests/xform_constprop_test.ml index 95c60a21..01e866ae 100644 --- a/tests/xform_constprop_test.ml +++ b/tests/xform_constprop_test.ml @@ -342,11 +342,11 @@ let constprop_tests : unit Alcotest.test_case list = "var i : Integer; function Foo(x : Integer) begin end" "case i of when 16 => Foo(i); - when 0x20 => Foo(i); + when 32 => Foo(i); endcase;" "case i of when 16 => Foo(16); - when 0x20 => Foo(32); + when 32 => Foo(32); endcase;"); ("case stmt bitvectors", `Quick, test_cp_stmts "var i : Bits(8); function Foo(x : Bits(8)) begin end" From 1efc3a9b942b3705a79c1b52ba38e179813c00d5 Mon Sep 17 00:00:00 2001 From: Alastair Reid Date: Mon, 27 Apr 2026 16:59:25 +0100 Subject: [PATCH 2/2] Hex-is-int: add compiler flag to enable/disable --- bin/iic.py | 33 +++++++++++++++++++++++---------- bin/iii.ml | 2 ++ libISA/isa_lexer.mll | 4 +++- tests/lit.cfg | 6 ++++-- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/bin/iic.py b/bin/iic.py index cf4c2112..dae9475d 100755 --- a/bin/iic.py +++ b/bin/iic.py @@ -253,10 +253,10 @@ def run(cmd): 'sc': ['-DASL_SC'] + sc_types_include, } -def get_c_flags(iii, backend): +def get_c_flags(iii, iii_flags, backend): if "opam" in iii: # iii has been installed: query it for flags - c_flags = subprocess.check_output([iii, "--print-c-flags"]).decode('utf-8').strip().split() + c_flags = subprocess.check_output([iii, "--print-c-flags"] + iii_flags).decode('utf-8').strip().split() else: # iii has not been installed so let's assume that it is being run # directly out of the build tree and the path looks like this ../_build/install/default/bin/iii @@ -276,10 +276,10 @@ def get_c_flags(iii, backend): 'sc': ["-lsystemc"], } -def get_ld_flags(iii, backend): +def get_ld_flags(iii, iii_flags, backend): if "opam" in iii: # iii has been installed: query it for flags - ld_flags = subprocess.check_output([iii, "--print-ld-flags"]).decode('utf-8').strip().split() + ld_flags = subprocess.check_output([iii, "--print-ld-flags"] + iii_flags).decode('utf-8').strip().split() else: # iii has not been installed so let's assume that it is being run # directly out of the build tree and the path looks like this ../_build/install/default/bin/iii @@ -422,11 +422,12 @@ def generate_config_file(config_file, exports, imports): print("}", file=f) report(f"# Generated configuration file {config_file}\n") -def run_iii(iii, args, isa_files, project_file, configurations): +def run_iii(iii, iii_flags, args, isa_files, project_file, configurations): iii_cmd = [ iii, "--batchmode", "--nobanner", ] + iii_cmd.extend(iii_flags) iii_cmd.append("--check-call-markers") iii_cmd.append("--check-exception-markers") if args.constraint_checks: print("Warning: ignoring --check-constraints") @@ -523,6 +524,7 @@ def main() -> int: parser.add_argument("--generate-cxx", help="generate C++ code", action="store_true", default=False) parser.add_argument("--import", dest="imports", help="import this symbol (C generation only)", action='append', default=[]) parser.add_argument("--line-info", help="insert line directives into C code", action=argparse.BooleanOptionalAction) + parser.add_argument("--language-version", help="select language version", choices=['2026-00', '2026-01'], default='2026-00') parser.add_argument("--new-ffi", help="use the new FFI", action="store_true", default=False) parser.add_argument("--ffi-integer", help="select type for Integer in new FFI (default: int)", choices=['int', 'int64_t'], default='int') parser.add_argument("--runtime-checks", help="perform runtime checks (array bounds, etc.)", action=argparse.BooleanOptionalAction) @@ -568,6 +570,15 @@ def main() -> int: print("Error: must specify --generate-cxx with --const-ref") exit(1) + # flags to select language version + language_flags = { + '2026-00': ['--lang-hex-is-int'], + '2026-01': ['--lang-hex-is-bits'], + } + + iii_flags = [] + iii_flags = language_flags[args.language_version] + # when running tests, we need to be able to use iii without having installed it iii = pathlib.Path(__file__).parent / "iii" if not iii.exists(): @@ -575,9 +586,9 @@ def main() -> int: iii = str(iii) if args.print_c_flags: - print(' '.join(get_c_flags(iii, args.backend))) + print(' '.join(get_c_flags(iii, iii_flags, args.backend))) elif args.print_ld_flags: - print(' '.join(get_ld_flags(iii, args.backend))) + print(' '.join(get_ld_flags(iii, iii_flags, args.backend))) elif not args.build: print(mk_script(args, args.output_dir)) elif args.run and args.backend == "interpreter": @@ -585,6 +596,7 @@ def main() -> int: iii, "--batchmode", "--nobanner", ] + iii_cmd.extend(iii_flags) iii_cmd.append("--check-call-markers") iii_cmd.append("--check-exception-markers") if args.constraint_checks: print("Warning: ignoring --check-constraints") @@ -608,6 +620,7 @@ def main() -> int: f"--exec=:generate_mlir --output-file={mlir_file}", "--exec=:quit", ] + iii_cmd.extend(iii_flags) iii_cmd.extend(args.isa_files) generate_config_file(config_file, ["main"] + args.exports, args.imports) run(iii_cmd) @@ -625,12 +638,12 @@ def main() -> int: generate_project(old_project_file, script) # deprecated generate_project(project_file, script) generate_config_file(config_filename, ["main"] + args.exports, args.imports) - run_iii(iii, args, args.isa_files, project_file, [config_filename]+args.configuration) + run_iii(iii, iii_flags, args, args.isa_files, project_file, [config_filename]+args.configuration) if args.show_final_isa: pass elif args.run: - c_flags = get_c_flags(iii, backend) - ld_flags = get_ld_flags(iii, backend) + c_flags = get_c_flags(iii, iii_flags, backend) + ld_flags = get_ld_flags(iii, iii_flags, backend) compile_and_link(args.generate_cxx, c_files, args.extra_c, exe_file, working_directory, c_flags, ld_flags) run([exe_file]) if not args.save_temps: shutil.rmtree(working_directory) diff --git a/bin/iii.ml b/bin/iii.ml index a3c1c55c..3f33bfae 100644 --- a/bin/iii.ml +++ b/bin/iii.ml @@ -408,6 +408,8 @@ let options = ("--batchmode", Arg.Set opt_batchmode, " Fail on error"); ("--legacy-prelude", Arg.Set opt_enable_legacy_prelude, " Enable use of legacy ISA function names"); ("--no-legacy-prelude", Arg.Clear opt_enable_legacy_prelude, " Disable use of legacy ISA function names"); + ("--lang-hex-is-bits", Arg.Set Isa_lexer.lang_hex_is_bits, " Hex literals have type Bits()"); + ("--lang-hex-is-int", Arg.Clear Isa_lexer.lang_hex_is_bits," Hex literals have type Integer"); ("--configuration", Arg.String Configuration.read_configuration_file, " Load JSON configuration file"); ("--exec", Arg.String add_exec, " Execute command"); diff --git a/libISA/isa_lexer.mll b/libISA/isa_lexer.mll index 1422277c..53d19042 100644 --- a/libISA/isa_lexer.mll +++ b/libISA/isa_lexer.mll @@ -10,6 +10,8 @@ open Isa_parser (* The type token is defined in parser.mli *) exception Eof +let lang_hex_is_bits = ref true + let keywords : (string * Isa_parser.token) list = [ ("UNSPECIFIED", UNSPECIFIED); ("_", UNDERSCORE); @@ -127,7 +129,7 @@ rule token = parse { BITSLIT(Primops.mkBits (int_of_string len) (Z.of_string_base 16 nibbles)) } | '0''x'(['0'-'9' 'A'-'F' 'a'-'f' '_']+ as nibbles) { - if true then + if !lang_hex_is_bits then let x = Utils.drop_chars nibbles '_' in BITSLIT(Primops.mkBits (4 * String.length x) (Z.of_string_base 16 x)) else diff --git a/tests/lit.cfg b/tests/lit.cfg index 2bec32ab..6ed59d3a 100644 --- a/tests/lit.cfg +++ b/tests/lit.cfg @@ -12,6 +12,8 @@ sc_types_dir = os.environ.get("SC_TYPES_DIR") xdsl_isa_dir = os.environ.get("XDSL_ISA_DIR") isa_CC = os.environ.get("ISA_CC") +lang_version = "2026-01" + # Lit configuration if backend is None: config.name = "ISA" @@ -34,8 +36,8 @@ if proc.returncode == 0 and "1" in proc.stdout: config.substitutions.append(('%backend', backend)) config.substitutions.append(('%iii', iii_bin_path)) -config.substitutions.append(('%aslrun', f"{iic_path} --backend={backend} -O0 --run")) -config.substitutions.append(('%aslopt', f"{iic_path} --show-final-isa")) +config.substitutions.append(('%aslrun', f"{iic_path} --language-version={lang_version} --backend={backend} -O0 --run")) +config.substitutions.append(('%aslopt', f"{iic_path} --language-version={lang_version} --show-final-isa")) config.environment["ISA_DIR"] = isa_path config.environment["ISA_PATH"] = f":{isa_path}:." if ac_types_dir: config.environment["AC_TYPES_DIR"] = ac_types_dir