diff --git a/src/riscv_generate.ml b/src/riscv_generate.ml index f03707f..05418c3 100644 --- a/src/riscv_generate.ml +++ b/src/riscv_generate.ml @@ -1,4 +1,5 @@ open Riscv_ssa +open Mtype (** Some MoonBit IR instructions carry optional compiler primitives in strange places. @@ -34,7 +35,7 @@ let traited_args = Hashtbl.create 64 let offsetof ty pos = Hashtbl.find offset_table (ty, pos) let is_trait ty = match ty with -| Mtype.T_trait _ -> true +| T_trait _ -> true | _ -> false let pointer_size = 8 @@ -50,22 +51,22 @@ let remove_space = String.map (fun c -> if c = ' ' then '_' else c) (* This is the size of their representations, not the actual size. *) let rec sizeof ty = match ty with -| Mtype.T_bool -> 1 -| Mtype.T_byte -> 1 -| Mtype.T_bytes -> pointer_size -| Mtype.T_char -> 1 -| Mtype.T_double -> 8 -| Mtype.T_float -> 4 -| Mtype.T_func _ -> pointer_size -| Mtype.T_int -> 4 -| Mtype.T_int64 -> 8 -| Mtype.T_string -> pointer_size -| Mtype.T_uint -> 4 -| Mtype.T_uint64 -> 8 -| Mtype.T_unit -> 0 -| Mtype.T_tuple _ -> pointer_size -| Mtype.T_constr id -> pointer_size -| Mtype.T_fixedarray _ -> pointer_size +| T_bool -> 1 +| T_byte -> 1 +| T_bytes -> pointer_size +| T_char -> 2 +| T_double -> 8 +| T_float -> 4 +| T_func _ -> pointer_size +| T_int -> 4 +| T_int64 -> 8 +| T_string -> pointer_size +| T_uint -> 4 +| T_uint64 -> 8 +| T_unit -> 0 +| T_tuple _ -> pointer_size +| T_constr id -> pointer_size +| T_fixedarray _ -> pointer_size | _ -> failwith "riscv_ssa.ml: cannot calculate size" (** Push the correct sequence of instruction based on primitives. *) @@ -137,7 +138,7 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = (match from, to_ with | I32, U8 | U32, U8 -> (* Discard higher bits by masking them away *) - let mask = new_temp Mtype.T_int in + let mask = new_temp T_int in Basic_vec.push ssa (AssignInt { rd = mask; imm = 255L }); Basic_vec.push ssa (And { rd; rs1 = arg; rs2 = mask }) @@ -146,8 +147,8 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = | Parray_make -> (* This should construct a struct like: *) (* struct { void* buf; int len; } *) - let buf = new_temp Mtype.T_bytes in - let len = new_temp Mtype.T_int in + let buf = new_temp T_bytes in + let len = new_temp T_int in let length = List.length args in let buf_size = if length = 0 then 0 else length * (sizeof (List.hd args).ty) in Basic_vec.push ssa (Malloc { rd; size = 12 }); @@ -172,9 +173,9 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = | _ -> failwith "riscv_ssa.ml: bad type in fixedarray_get_item") in let size = sizeof ty in - let addr = new_temp Mtype.T_bytes in - let sz = new_temp Mtype.T_int in - let offset = new_temp Mtype.T_int in + let addr = new_temp T_bytes in + let sz = new_temp T_int in + let offset = new_temp T_int in (* Same as `rd = *(arr + sizeof(T) * index)` *) Basic_vec.push ssa (AssignInt { rd = sz; imm = Int64.of_int size; }); @@ -195,8 +196,8 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = | _ -> failwith "riscv_ssa.ml: bad type in fixedarray_make") in let size = sizeof ty in - let sz = new_temp Mtype.T_int in - let length = new_temp Mtype.T_int in + let sz = new_temp T_int in + let length = new_temp T_int in (* Same as `rd = malloc(sizeof(T) * len)` *) Basic_vec.push ssa (AssignInt { rd = sz; imm = Int64.of_int size; }); Basic_vec.push ssa (Mul { rd = length; rs1 = sz; rs2 = len }); @@ -212,9 +213,9 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = let arg = List.hd args in (* Temporaries in SSA *) - let vtb = new_temp Mtype.T_bytes in - let fn_addr = new_temp Mtype.T_bytes in - let load = new_temp Mtype.T_int in + let vtb = new_temp T_bytes in + let fn_addr = new_temp T_bytes in + let load = new_temp T_int in Basic_vec.push ssa (Load { rd = vtb; rs = arg; offset = -pointer_size; byte = pointer_size }); Basic_vec.push ssa (AssignInt { rd = load; imm = Int64.of_int vtb_offset }); @@ -222,25 +223,37 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = (* The whole set of args (including self) is needed. *) Basic_vec.push ssa (CallIndirect { rd; rs = fn_addr; args }) - (* Be cautious that each element is 1 byte long. *) - (* Strings and bytes are always treated the same. *) - | Pgetstringitem | Pgetbytesitem -> let str = List.nth args 0 in let i = List.nth args 1 in - let altered = new_temp Mtype.T_string in + let altered = new_temp T_string in Basic_vec.push ssa (Add { rd = altered; rs1 = str; rs2 = i }); Basic_vec.push ssa (Load { rd; rs = altered; offset = 0; byte = 1 }) | Psetbytesitem -> let str = List.nth args 0 in let i = List.nth args 1 in + let item = List.nth args 2 in - let altered = new_temp Mtype.T_string in + let altered = new_temp T_string in Basic_vec.push ssa (Add { rd = altered; rs1 = str; rs2 = i }); - Basic_vec.push ssa (Store { rd; rs = altered; offset = 0; byte = 1 }) + Basic_vec.push ssa (Store { rd = item; rs = altered; offset = 0; byte = 1 }) + (* Be cautious that each `char` is 2 bytes long, which is extremely counter-intuitive. *) + | Pgetstringitem -> + let str = List.nth args 0 in + let i = List.nth args 1 in + + let two = new_temp T_int in + let offset = new_temp T_int in + let altered = new_temp T_string in + Basic_vec.push ssa (AssignInt { rd = two; imm = 2L }); + Basic_vec.push ssa (Mul { rd = offset; rs1 = i; rs2 = two }); + Basic_vec.push ssa (Add { rd = altered; rs1 = str; rs2 = offset }); + Basic_vec.push ssa (Load { rd; rs = altered; offset = 0; byte = 2 }) + + (* Length are both stored at the same place for these arrays. *) | Pstringlength | Pbyteslength -> let bytes = List.hd args in @@ -253,10 +266,10 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = let init = List.nth args 1 in (* Let the pointer point to beginning of data, rather than the length section *) - let memory = new_temp Mtype.T_bytes in - let unused = new_temp Mtype.T_unit in - let int_sz = new_temp Mtype.T_int in - let new_len = new_temp Mtype.T_int in + let memory = new_temp T_bytes in + let unused = new_temp T_unit in + let int_sz = new_temp T_int in + let new_len = new_temp T_int in Basic_vec.push ssa (AssignInt { rd = int_sz; imm = 4L }); Basic_vec.push ssa (Add { rd = new_len; rs1 = len; rs2 = int_sz }); Basic_vec.push ssa (CallExtern { rd = memory; fn = "malloc"; args = [ new_len ] }); @@ -279,21 +292,21 @@ let deal_with_prim ssa rd (prim: Primitive.prim) args = (** Extract information from types and store them in global variables. *) -let update_types ({ defs; _ }: Mtype.defs) = - let types = Mtype.Id_hash.to_list defs in +let update_types ({ defs; _ }: defs) = + let types = Id_hash.to_list defs in let visit (name, info) = match info with - | Mtype.Placeholder -> () - | Mtype.Externref -> () + | Placeholder -> () + | Externref -> () (* We don't care about declarations in traits. *) - | Mtype.Trait _ -> () + | Trait _ -> () (* Calculate offset of fields in record types. *) - | Mtype.Record { fields } -> - let ty = Mtype.T_constr name in - let extract (x: Mtype.field_info) = x.field_type in + | Record { fields } -> + let ty = T_constr name in + let extract (x: field_info) = x.field_type in let field_types = List.map extract fields in let field_sizes = List.map sizeof field_types in let offset = ref 0 in @@ -317,10 +330,10 @@ let record_traits (methods: Object_util.t) = let vtb_size = Hashtbl.find trait_table ty |> Basic_vec.length in - (* Note: traits are originally converted from Stype.T_trait to Mtype.T_trait, *) + (* Note: traits are originally converted from Stype.T_trait to T_trait, *) (* and the former takes a Basic_type_path, as expected. *) (* However, the conversion function needs additional information which is unknown at this stage. *) - Hashtbl.add trait_offset (ty, Mtype.T_trait trait_name) vtb_size; + Hashtbl.add trait_offset (ty, T_trait trait_name) vtb_size; Basic_vec.append (Hashtbl.find trait_table ty) (Basic_vec.of_list methods) ) @@ -369,12 +382,12 @@ let rec do_convert ssa (expr: Mcore.expr) = let ty = obj.ty in let trait_name = Basic_type_path.sexp_of_t trait |> S.to_string in - let delta = Hashtbl.find trait_offset (ty, Mtype.T_trait trait_name) in + let delta = Hashtbl.find trait_offset (ty, T_trait trait_name) in (* Temporary variables used in SSA *) - let load = new_temp Mtype.T_int in - let vtb = new_temp Mtype.T_bytes in - let altered = new_temp Mtype.T_bytes in + let load = new_temp T_int in + let vtb = new_temp T_bytes in + let altered = new_temp T_bytes in (* Alter the vtable offset according to the trait *) Basic_vec.push ssa (Load { rd = vtb; rs = obj; offset = 0; byte = pointer_size }); @@ -394,8 +407,8 @@ let rec do_convert ssa (expr: Mcore.expr) = let ifso = new_label "sequand_if_" in let ifnot = new_label "sequand_else_" in let ifexit = new_label "sequand_exit_" in - let t1 = new_temp Mtype.T_bool in - let t2 = new_temp Mtype.T_bool in + let t1 = new_temp T_bool in + let t2 = new_temp T_bool in let cond = do_convert ssa rs1 in Basic_vec.push ssa (Branch { cond; ifso; ifnot }); @@ -418,8 +431,8 @@ let rec do_convert ssa (expr: Mcore.expr) = let ifso = new_label "sequor_if_" in let ifnot = new_label "sequor_else_" in let ifexit = new_label "sequor_exit_" in - let t1 = new_temp Mtype.T_bool in - let t2 = new_temp Mtype.T_bool in + let t1 = new_temp T_bool in + let t2 = new_temp T_bool in let cond = do_convert ssa rs1 in Basic_vec.push ssa (Branch { cond; ifso; ifnot }); @@ -446,7 +459,7 @@ let rec do_convert ssa (expr: Mcore.expr) = (match name with | Pmutable_ident _ -> (* We use `bytes` to represent arbitrary pointers. *) - let space = new_temp Mtype.T_bytes in + let space = new_temp T_bytes in let rd = { name = Ident.to_string name; ty = rs.ty } in Basic_vec.push ssa (Malloc { rd = space; size = sizeof rs.ty }); Basic_vec.push ssa (Assign { rd; rs = space }); @@ -475,9 +488,9 @@ let rec do_convert ssa (expr: Mcore.expr) = (* Trait themselves can't derive another trait, *) (* so no worries about diamond inheritance *) - let load = new_temp Mtype.T_int in - let vtb = new_temp Mtype.T_bytes in - let altered = new_temp Mtype.T_bytes in + let load = new_temp T_int in + let vtb = new_temp T_bytes in + let altered = new_temp T_bytes in let offset = -pointer_size in let byte = pointer_size in @@ -689,7 +702,7 @@ let rec do_convert ssa (expr: Mcore.expr) = (* Assigns mutable variables. *) | Cexpr_assign { var; expr; ty } -> let rd = do_convert ssa expr in - let rs = { name = Ident.to_string var; ty = Mtype.T_bytes } in + let rs = { name = Ident.to_string var; ty = T_bytes } in Basic_vec.push ssa (Store { rd; rs; offset = 0; byte = sizeof rd.ty }); unit @@ -703,7 +716,7 @@ let rec do_convert ssa (expr: Mcore.expr) = (if has_vtable then let beginning = new_temp ty in - let load = new_temp Mtype.T_int in + let load = new_temp T_int in (* We construct vtable before every field *) (* and let `rd` point at where fields start *) @@ -713,8 +726,8 @@ let rec do_convert ssa (expr: Mcore.expr) = Basic_vec.push ssa (Add { rd; rs1 = beginning; rs2 = load }); (* Load in vtable *) - let vtb = new_temp Mtype.T_bytes in - let label = Printf.sprintf "vtable_%s" (Mtype.to_string ty |> remove_space) in + let vtb = new_temp T_bytes in + let label = Printf.sprintf "vtable_%s" (to_string ty |> remove_space) in Basic_vec.push ssa (AssignLabel { rd = vtb; imm = label }); Basic_vec.push ssa (Store { rd = vtb; rs = rd; offset = -pointer_size; byte = pointer_size }) else @@ -742,7 +755,7 @@ let rec do_convert ssa (expr: Mcore.expr) = let rd = new_temp ty in let tys = (match ty with - | Mtype.T_tuple { tys } -> tys + | T_tuple { tys } -> tys | _ -> failwith "riscv_ssa.ml: bad tuple") in @@ -800,9 +813,29 @@ let rec do_convert ssa (expr: Mcore.expr) = | Cexpr_const { c; ty; _ } -> let rd = new_temp ty in let instruction = (match c with - (* This is not C-style string. \0 is allowed to appear. *) - (* In this case, we treat strings and bytes as the same thing. *) - | C_string v + (* Note each element of string is 2 bytes long. TODO *) + | C_string v -> + let label = Printf.sprintf "str_%d" !slot in + let vals = String.to_seq v |> List.of_seq in + let len = String.length v |> Int.to_string in + let vec = Basic_vec.empty () in + + List.iter (fun x -> + Basic_vec.push vec (Char.code x); + Basic_vec.push vec 0) vals; + let values = len :: Basic_vec.map_into_list vec Int.to_string in + + slot := !slot + 1; + Basic_vec.push global_inst (ExtArray { label; values }); + + (* Let the pointer point to beginning of data, rather than the length section *) + let beginning = new_temp T_bytes in + let four = new_temp T_int in + Basic_vec.push ssa (ExtArray { label; values }); + Basic_vec.push ssa (AssignLabel { rd = beginning; imm = label; }); + Basic_vec.push ssa (AssignInt { rd = four; imm = 4L }); + Add { rd; rs1 = beginning; rs2 = four } + | C_bytes { v; _ } -> let label = Printf.sprintf "bytes_%d" !slot in let vals = String.to_seq v |> List.of_seq |> List.map (fun x -> Char.code x |> Int.to_string) in @@ -813,8 +846,8 @@ let rec do_convert ssa (expr: Mcore.expr) = Basic_vec.push global_inst (ExtArray { label; values }); (* Let the pointer point to beginning of data, rather than the length section *) - let beginning = new_temp Mtype.T_bytes in - let four = new_temp Mtype.T_int in + let beginning = new_temp T_bytes in + let four = new_temp T_int in Basic_vec.push ssa (ExtArray { label; values }); Basic_vec.push ssa (AssignLabel { rd = beginning; imm = label; }); Basic_vec.push ssa (AssignInt { rd = four; imm = 4L }); @@ -844,7 +877,7 @@ let rec do_convert ssa (expr: Mcore.expr) = let generate_vtables () = Hashtbl.iter (fun ty methods -> - let label_raw = Printf.sprintf "vtable_%s" (Mtype.to_string ty) in + let label_raw = Printf.sprintf "vtable_%s" (to_string ty) in let label = remove_space label_raw in Basic_vec.push global_inst (ExtArray { label; values = Basic_vec.to_list methods }) ) trait_table @@ -934,7 +967,7 @@ let ssa_of_mcore (core: Mcore.t) = in (* Add _start *) - let unused = new_temp Mtype.T_unit in + let unused = new_temp T_unit in Basic_vec.push _start (Call { rd = unused; fn = "main"; args = [] }); Basic_vec.push _start (Return unused); diff --git a/src/riscv_internals.ml b/src/riscv_internals.ml index 6dd3349..7e1c696 100644 --- a/src/riscv_internals.ml +++ b/src/riscv_internals.ml @@ -14,6 +14,7 @@ let add fn fn_decl = Hashtbl.add internals fn (fn_decl fn) Performs a byte copy from src to dst. fn unsafe_bytes_blit (dst, dst_offset, src, src_offset, length) { + ; copy add %1 dst dst_offset add %2 src src_offset call_libc %3 memcpy %1 %2 length @@ -46,11 +47,21 @@ let unsafe_bytes_blit fn = Performs a substring operation, but returns a new copy. Note we need to store length at the beginning. +Since each string character is 2 bytes long, the string length +is actually half of the argument `length`. + fn unsafe_bytes_sub_string (src, offset, length) { + ; allocate space li %1 4 add %2 length %1 call dst malloc %2 - sw length dst + + ; divide length by 2 + li %6 1 + shr %7 length %6 + sw %7 dst + + ; copy add %3 dst %1 add %4 src offset call %5 memcpy %3 %4 length @@ -70,15 +81,24 @@ let unsafe_bytes_sub_string fn = let _3 = new_temp T_bytes in let _4 = new_temp T_bytes in let _5 = new_temp T_unit in + let _6 = new_temp T_int in + let _7 = new_temp T_int in let dst = new_temp T_bytes in let body = [ + (* Allocate space *) AssignInt { rd = _1; imm = 4L }; Add { rd = _2; rs1 = length; rs2 = _1 }; CallExtern { rd = dst; fn = "malloc"; args = [ _2 ] }; - Store { rd = length; rs = dst; offset = 0; byte = 4 }; + + (* Divide length by 2 *) + AssignInt { rd = _6; imm = 1L }; + Shr { rd = _7; rs1 = length; rs2 = _6 }; + Store { rd = _7; rs = dst; offset = 0; byte = 4 }; + + (* Copy *) Add { rd = _3; rs1 = dst; rs2 = _1 }; Add { rd = _4; rs1 = src; rs2 = offset }; - CallExtern { rd = _5; fn = "memcpy"; args = [ _3; _4 ] }; + CallExtern { rd = _5; fn = "memcpy"; args = [ _3; _4; length ] }; Return _3; ] in (args, body) diff --git a/test.py b/test.py index ec2b3a5..dcf792a 100755 --- a/test.py +++ b/test.py @@ -20,6 +20,7 @@ def __exit__(self, exc_type, exc_value, traceback): parser.add_argument("-w", "--wasm", action="store_true", help="build to WASM rather than RISC-V") parser.add_argument("-i", "--build-index", action="store_true", help="build OCaml index and exit") parser.add_argument("-b", "--build-only", action="store_true", help="build without testing") +parser.add_argument("-v", "--verbose", action="store_true", help="interpreter outputs detailed values") args = parser.parse_args() @@ -32,12 +33,16 @@ def __exit__(self, exc_type, exc_value, traceback): core = "~/.moon/lib/core" bundled = f"{core}/target/wasm-gc/release/bundle" -src = "basic" if args.debug: debug = "OCAMLRUNPARAM=b" else: debug = "" + +if args.verbose: + verbose = "-DVERBOSE" +else: + verbose = "" def try_remove(path): if os.path.exists(path): @@ -54,7 +59,7 @@ def try_remove(path): os.system("dune build -p moonbit-lang") print("Building SSA interpreter...") -os.system("clang++ -std=c++20 -fuse-ld=lld test/interpreter.cpp -Wall -g -o test/build/interpreter") +os.system(f"clang++ -std=c++20 {verbose} test/interpreter.cpp -Wall -g -o test/build/interpreter") if args.build_only: print("Done.") @@ -66,19 +71,25 @@ def try_remove(path): with DirContext("test"): os.makedirs("build", exist_ok=True); + + cases = os.listdir("src"); + + for src in cases: + print(f"Execute task: {src}") + # Remove all previously compiled files. + try_remove(f"{src}.core") + try_remove(f"{src}.mi") + try_remove(f"{dest}") + + # Note build-package is ignorant of target. It builds to a common IR. + os.system(f"moonc build-package src/{src}/{src}.mbt -is-main -std-path {bundled} -o build/{src}.core") + + # Linkage emits target code. + os.system(f"{debug} moonc link-core {bundled}/core.core build/{src}.core -o build/{dest} -pkg-config-path {src}/moon.pkg.json -pkg-sources {core}:{src} -target {target}") + + # Test. + os.system(f"build/interpreter build/{dest}.ssa > build/output.txt") + diff = os.system(f"diff build/output.txt src/{src}/{src}.ans") - print(f"Execute task: {src}") - # Remove all previously compiled files. - try_remove(f"{src}.core") - try_remove(f"{src}.mi") - try_remove(f"{dest}") - - # Note build-package is ignorant of target. It builds to a common IR. - os.system(f"moonc build-package src/{src}/{src}.mbt -is-main -std-path {bundled} -o build/{src}.core") - - # Linkage emits target code. - os.system(f"{debug} moonc link-core {bundled}/core.core build/{src}.core -o build/{dest} -pkg-config-path {src}/moon.pkg.json -pkg-sources {core}:{src} -target {target}") - - # Test. - os.system(f"build/interpreter build/{dest}.ssa > build/output.txt") - os.system(f"diff build/output.txt src/{src}/{src}.ans") \ No newline at end of file + if diff == 0: + print("Passed.") \ No newline at end of file diff --git a/test/interpreter.cpp b/test/interpreter.cpp index 671b04f..d47bb42 100644 --- a/test/interpreter.cpp +++ b/test/interpreter.cpp @@ -59,6 +59,14 @@ int int_of(std::string s) { #define MEM(name, type) std::make_pair(name, [](uint64_t x, int offset) { return *((type*) (x + offset)); }) #define VAL(i) regs[args[i]] +#ifdef VERBOSE +#define OUTPUT(name, value) std::cerr << "\t" << name << " = " << value << "\n\n" +#define SAY(str) std::cerr << str << "\n" +#else +#define OUTPUT(name, value) +#define SAY(str) +#endif + // Argument `label` is where we start interpreting. uint64_t interpret(std::string label) { static std::map> rtype = { @@ -91,19 +99,24 @@ uint64_t interpret(std::string label) { for (;;) { for (auto x : blocks[label]) { - std::cerr << x << std::endl; + SAY(x); auto args = split(x, " "); auto op = args[0]; if (rtype.contains(op)) { VAL(1) = rtype[op](VAL(2), VAL(3)); - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } if (load.contains(op)) { VAL(1) = load[op](VAL(2), int_of(args[3])); - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); + continue; + } + + if (op == "neg") { + VAL(1) = -VAL(2); continue; } @@ -127,23 +140,23 @@ uint64_t interpret(std::string label) { auto fn = args[2]; for (int i = 0; i < fns[fn].size(); i++) { regs[fns[fn][i]] = VAL(i + 3); - std::cerr << fns[fn][i] << " <- " << VAL(i + 3) << "\n"; + OUTPUT(fns[fn][i], VAL(i + 3)); } VAL(1) = interpret(fn); - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } if (op == "call_indirect") { // Remember, we store function names in the pointer std::string fn(*(char**) VAL(2)); - std::cerr << "jump to " << fn << "\n"; + SAY("jump to " << fn); for (int i = 0; i < fns[fn].size(); i++) { regs[fns[fn][i]] = VAL(i + 3); - std::cerr << fns[fn][i] << " <- " << VAL(i + 3) << "\n"; + OUTPUT(fns[fn][i], VAL(i + 3)); } VAL(1) = interpret(fn); - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } @@ -156,29 +169,42 @@ uint64_t interpret(std::string label) { std::string utf8_string = convert.to_bytes(utf16_str); std::cout << utf8_string << std::endl; + continue; } - if (args[2] == "malloc") + if (args[2] == "malloc") { VAL(1) = (uint64_t) new char[VAL(3)]; + continue; + } - if (args[2] == "strlen") + if (args[2] == "strlen") { VAL(1) = (uint64_t) strlen((char*) VAL(3)); + continue; + } - if (args[2] == "memset") - memset((void*) VAL(3), VAL(4), VAL(5)), + if (args[2] == "memset") { + memset((void*) VAL(3), VAL(4), VAL(5)); VAL(1) = unit; + continue; + } + + if (args[2] == "memcpy") { + memcpy((void*) VAL(3), (void*) VAL(4), VAL(5)); + continue; + } if (args[2] == "abort") abort(); - continue; + std::cerr << "Unknown libc call: " << args[2] << std::endl; + exit(3); } if (op == "malloc") { auto len = int_of(args[2]); VAL(1) = (uint64_t) new char[len]; - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } @@ -191,7 +217,7 @@ uint64_t interpret(std::string label) { if (plabel == prev) { VAL(1) = regs[var]; is_bad = false; - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); break; } } @@ -207,13 +233,13 @@ uint64_t interpret(std::string label) { auto rs1 = int_of(args[2]); VAL(1) = rs1; - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } if (op == "mv" || op == "la") { VAL(1) = VAL(2); - std::cerr << args[1] << " = " << VAL(1) << "\n\n"; + OUTPUT(args[1], VAL(1)); continue; } diff --git a/test/src/control/control.ans b/test/src/control/control.ans new file mode 100644 index 0000000..8d2470d --- /dev/null +++ b/test/src/control/control.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +0 +1 +2 +3 +4 diff --git a/test/src/control/control.mbt b/test/src/control/control.mbt new file mode 100644 index 0000000..b053512 --- /dev/null +++ b/test/src/control/control.mbt @@ -0,0 +1,9 @@ +fn main { + for i = 0; i < 10; i = i + 1 { + if (i >= 5) { + println(i - 5) + } else { + println(i) + } + } +} \ No newline at end of file