diff --git a/compiler/bin-wasm_of_ocaml/cmd_arg.ml b/compiler/bin-wasm_of_ocaml/cmd_arg.ml index 0e043591d..8c2e32878 100644 --- a/compiler/bin-wasm_of_ocaml/cmd_arg.ml +++ b/compiler/bin-wasm_of_ocaml/cmd_arg.ml @@ -28,6 +28,7 @@ type t = ; runtime_files : string list ; output_file : string * bool ; input_file : string + ; enable_source_maps : bool ; params : (string * string) list } @@ -50,11 +51,11 @@ let options = Arg.(value & opt (some (enum profile)) None & info [ "opt" ] ~docv:"NUM" ~doc) in let no_sourcemap = - let doc = "Currently ignored (for compatibility with Js_of_ocaml)." in + let doc = "Disable sourcemap output." in Arg.(value & flag & info [ "no-sourcemap"; "no-source-map" ] ~doc) in let sourcemap = - let doc = "Currently ignored (for compatibility with Js_of_ocaml)." in + let doc = "Output source locations in a separate sourcemap file." in Arg.(value & flag & info [ "sourcemap"; "source-map" ] ~doc) in let sourcemap_inline_in_js = @@ -69,7 +70,16 @@ let options = & opt_all (list (pair ~sep:'=' (enum all) string)) [] & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) in - let build_t common set_param profile _ _ _ output_file input_file runtime_files = + let build_t + common + set_param + profile + sourcemap + no_sourcemap + _ + output_file + input_file + runtime_files = let chop_extension s = try Filename.chop_extension s with Invalid_argument _ -> s in let output_file = match output_file with @@ -77,7 +87,15 @@ let options = | None -> chop_extension input_file ^ ".js", false in let params : (string * string) list = List.flatten set_param in - `Ok { common; params; profile; output_file; input_file; runtime_files } + let enable_source_maps = not no_sourcemap && sourcemap in + `Ok { + common; + params; + profile; + output_file; + input_file; + runtime_files; + enable_source_maps } in let t = Term.( @@ -85,8 +103,8 @@ let options = $ Jsoo_cmdline.Arg.t $ set_param $ profile - $ no_sourcemap $ sourcemap + $ no_sourcemap $ sourcemap_inline_in_js $ output_file $ input_file diff --git a/compiler/bin-wasm_of_ocaml/cmd_arg.mli b/compiler/bin-wasm_of_ocaml/cmd_arg.mli index 1f0c36cda..d5224169b 100644 --- a/compiler/bin-wasm_of_ocaml/cmd_arg.mli +++ b/compiler/bin-wasm_of_ocaml/cmd_arg.mli @@ -26,6 +26,7 @@ type t = ; runtime_files : string list ; output_file : string * bool ; input_file : string + ; enable_source_maps : bool ; params : (string * string) list } diff --git a/compiler/bin-wasm_of_ocaml/compile.ml b/compiler/bin-wasm_of_ocaml/compile.ml index 386bebcf0..eba32f026 100644 --- a/compiler/bin-wasm_of_ocaml/compile.ml +++ b/compiler/bin-wasm_of_ocaml/compile.ml @@ -83,15 +83,19 @@ let common_binaryen_options () = in if Config.Flag.pretty () then "-g" :: l else l -let link runtime_files input_file output_file = +let link ~enable_source_maps runtime_files input_file output_file = command ("wasm-merge" :: (common_binaryen_options () @ List.flatten (List.map - ~f:(fun runtime_file -> [ Filename.quote runtime_file; "env" ]) + ~f:(fun runtime_file -> + [ Filename.quote runtime_file; "env" ]) runtime_files) - @ [ Filename.quote input_file; "exec"; "-o"; Filename.quote output_file ])) + @ [ Filename.quote input_file; "exec"; "-o"; Filename.quote output_file ] + @ (if enable_source_maps then + [ "--output-source-map"; Filename.quote (output_file ^ ".map") ] + else []))) let generate_dependencies primitives = Yojson.Basic.to_string @@ -119,7 +123,7 @@ let filter_unused_primitives primitives usage_file = with End_of_file -> ()); !s -let dead_code_elimination in_file out_file = +let dead_code_elimination ~enable_source_maps in_file out_file = with_intermediate_file (Filename.temp_file "deps" ".json") @@ fun deps_file -> with_intermediate_file (Filename.temp_file "usage" ".txt") @@ -131,21 +135,27 @@ let dead_code_elimination in_file out_file = :: (common_binaryen_options () @ [ "--graph-file" ; Filename.quote deps_file - ; Filename.quote in_file - ; "-o" - ; Filename.quote out_file - ; ">" + ; Filename.quote in_file ] + @ (if enable_source_maps then + [ "--input-source-map"; Filename.quote (in_file ^ ".map") ] + else []) + @ [ "-o" + ; Filename.quote out_file ] + @ (if enable_source_maps then + [ "--output-source-map"; Filename.quote (out_file ^ ".map") ] + else []) + @ [ ">" ; Filename.quote usage_file ])); filter_unused_primitives primitives usage_file let optimization_options = - [| [ "-O2"; "--skip-pass=inlining-optimizing" ] + [| [ "--simplify-locals-notee-nostructure"; "--vacuum"; "--reorder-locals"] ; [ "-O2"; "--skip-pass=inlining-optimizing"; "--traps-never-happen" ] ; [ "-O3"; "--traps-never-happen" ] |] -let optimize ~profile in_file out_file = +let optimize ~profile ?sourcemap_file in_file out_file = let level = match profile with | None -> 1 @@ -155,19 +165,48 @@ let optimize ~profile in_file out_file = ("wasm-opt" :: (common_binaryen_options () @ optimization_options.(level - 1) - @ [ Filename.quote in_file; "-o"; Filename.quote out_file ])) + @ [ Filename.quote in_file; "-o"; Filename.quote out_file ]) + @ (match sourcemap_file with + | Some sourcemap_file -> + [ "--input-source-map" + ; Filename.quote (in_file ^ ".map") + ; "--output-source-map" + ; Filename.quote sourcemap_file + ; "--output-source-map-url" + ; Filename.quote sourcemap_file ] + | None -> [])) -let link_and_optimize ~profile runtime_wasm_files wat_file output_file = +let link_and_optimize + ~profile + ?sourcemap_file + runtime_wasm_files + wat_file + output_file = + let enable_source_maps = Option.is_some sourcemap_file in with_intermediate_file (Filename.temp_file "runtime" ".wasm") @@ fun runtime_file -> write_file runtime_file Wa_runtime.wasm_runtime; with_intermediate_file (Filename.temp_file "wasm-merged" ".wasm") @@ fun temp_file -> - link (runtime_file :: runtime_wasm_files) wat_file temp_file; + link ~enable_source_maps (runtime_file :: runtime_wasm_files) wat_file temp_file; with_intermediate_file (Filename.temp_file "wasm-dce" ".wasm") @@ fun temp_file' -> - let primitives = dead_code_elimination temp_file temp_file' in - optimize ~profile temp_file' output_file; + let primitives = dead_code_elimination ~enable_source_maps temp_file temp_file' in + optimize ~profile ?sourcemap_file temp_file' output_file; + (* Add source file contents to source map *) + Option.iter sourcemap_file ~f:(fun sourcemap_file -> + let open Source_map in + let source_map, mappings = Source_map_io.of_file_no_mappings sourcemap_file in + assert (List.is_empty (Option.value source_map.sources_content ~default:[])); + let sources_content = + Some ( + List.map source_map.sources ~f:(fun file -> + if Sys.file_exists file && not (Sys.is_directory file) then + Some (Fs.read_file file) + else None)) + in + let source_map = { source_map with sources_content } in + Source_map_io.to_file ?mappings source_map ~file:sourcemap_file); primitives let escape_string s = @@ -274,7 +313,14 @@ let build_js_runtime primitives (strings, fragments) wasm_file output_file = ^ trim_semi (Buffer.contents fragment_buffer) ^ String.sub s ~pos:(k + 7) ~len:(String.length s - k - 7)) -let run { Cmd_arg.common; profile; runtime_files; input_file; output_file; params } = +let run { + Cmd_arg.common; + profile; + runtime_files; + input_file; + output_file; + enable_source_maps; + params } = Jsoo_cmdline.Arg.eval common; Wa_generate.init (); let output_file = fst output_file in @@ -364,7 +410,13 @@ let run { Cmd_arg.common; profile; runtime_files; input_file; output_file; param @@ fun tmp_wasm_file -> let strings = output_gen wat_file (output code ~standalone:true) in let primitives = - link_and_optimize ~profile runtime_wasm_files wat_file tmp_wasm_file + link_and_optimize + ~profile + ?sourcemap_file: + (if enable_source_maps then Some (wasm_file ^ ".map") else None) + runtime_wasm_files + wat_file + tmp_wasm_file in build_js_runtime primitives strings wasm_file output_file | `Cmo _ | `Cma _ -> assert false); diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index f3cd7837e..8c15b1650 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -618,7 +618,7 @@ let full ~target ~standalone ~wrap_with_fun ~profile ~linkall ~source_map d p = source_map, ([], []) | `Wasm ch -> let (p, live_vars), _, in_cps = r in - None, Wa_generate.f ch ~live_vars ~in_cps p + None, Wa_generate.f ~debug:d ch ~live_vars ~in_cps p let full_no_source_map ~target ~standalone ~wrap_with_fun ~profile ~linkall d p = let (_ : Source_map.t option * _) = diff --git a/compiler/lib/generate.ml b/compiler/lib/generate.ml index 202c4773c..a6146d1e5 100644 --- a/compiler/lib/generate.ml +++ b/compiler/lib/generate.ml @@ -341,11 +341,14 @@ let bool e = J.ECond (e, one, zero) (****) -let source_location ctx ?force (pc : Code.loc) = - match Parse_bytecode.Debug.find_loc ctx.Ctx.debug ?force pc with +let source_location debug ?force (pc : Code.loc) = + match Parse_bytecode.Debug.find_loc debug ?force pc with | Some pi -> J.Pi pi | None -> J.N +let source_location_ctx ctx ?force (pc : Code.loc) = + source_location ctx.Ctx.debug ?force pc + (****) let float_const f = J.ENum (J.Num.of_float f) @@ -1240,13 +1243,13 @@ let rec translate_expr ctx queue loc x e level : _ * J.statement_list = let (px, cx), queue = access_queue queue x in (Mlvalue.Block.field cx n, or_p px mutable_p, queue), [] | Closure (args, ((pc, _) as cont)) -> - let loc = source_location ctx ~force:After (After pc) in + let loc = source_location_ctx ctx ~force:After (After pc) in let clo = compile_closure ctx cont in let clo = match clo with | (st, x) :: rem -> let loc = - match x, source_location ctx (Before pc) with + match x, source_location_ctx ctx (Before pc) with | (J.U | J.N), (J.U | J.N) -> J.U | x, (J.U | J.N) -> x | (J.U | J.N), x -> x @@ -1495,14 +1498,14 @@ and translate_instr ctx expr_queue instr = let instr, pc = instr in match instr with | Assign (x, y) -> - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in let (_py, cy), expr_queue = access_queue expr_queue y in flush_queue expr_queue mutator_p [ J.Expression_statement (J.EBin (J.Eq, J.EVar (J.V x), cy)), loc ] | Let (x, e) -> ( - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in let (ce, prop, expr_queue), instrs = translate_expr ctx expr_queue loc x e 0 in let keep_name x = match Code.Var.get_name x with @@ -1533,7 +1536,7 @@ and translate_instr ctx expr_queue instr = prop (instrs @ [ J.variable_declaration [ J.V x, (ce, loc) ], loc ])) | Set_field (x, n, y) -> - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in let (_px, cx), expr_queue = access_queue expr_queue x in let (_py, cy), expr_queue = access_queue expr_queue y in flush_queue @@ -1541,7 +1544,7 @@ and translate_instr ctx expr_queue instr = mutator_p [ J.Expression_statement (J.EBin (J.Eq, Mlvalue.Block.field cx n, cy)), loc ] | Offset_ref (x, 1) -> - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in (* FIX: may overflow.. *) let (_px, cx), expr_queue = access_queue expr_queue x in flush_queue @@ -1549,7 +1552,7 @@ and translate_instr ctx expr_queue instr = mutator_p [ J.Expression_statement (J.EUn (J.IncrA, Mlvalue.Block.field cx 0)), loc ] | Offset_ref (x, n) -> - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in (* FIX: may overflow.. *) let (_px, cx), expr_queue = access_queue expr_queue x in flush_queue @@ -1558,7 +1561,7 @@ and translate_instr ctx expr_queue instr = [ J.Expression_statement (J.EBin (J.PlusEq, Mlvalue.Block.field cx 0, int n)), loc ] | Array_set (x, y, z) -> - let loc = source_location ctx pc in + let loc = source_location_ctx ctx pc in let (_px, cx), expr_queue = access_queue expr_queue x in let (_py, cy), expr_queue = access_queue expr_queue y in let (_pz, cz), expr_queue = access_queue expr_queue z in @@ -1619,7 +1622,7 @@ and compile_block st queue (pc : Addr.t) loop_stack frontier interm = else ( if debug () then Format.eprintf "break;@;}@]@,"; body @ [ J.Break_statement None, J.N ])) ) - , source_location st.ctx (Code.location_of_pc pc) ) + , source_location_ctx st.ctx (Code.location_of_pc pc) ) in let label = if !lab_used then Some lab else None in let for_loop = @@ -1854,7 +1857,7 @@ and compile_conditional st queue last loop_stack backs frontier interm = | Stop -> Format.eprintf "stop;@;" | Cond (x, _, _) -> Format.eprintf "@[cond(%a){@;" Code.Var.print x | Switch (x, _, _) -> Format.eprintf "@[switch(%a){@;" Code.Var.print x); - let loc = source_location st.ctx pc in + let loc = source_location_ctx st.ctx pc in let res = match last with | Return x -> diff --git a/compiler/lib/generate.mli b/compiler/lib/generate.mli index 66053fdc2..21bb63ff5 100644 --- a/compiler/lib/generate.mli +++ b/compiler/lib/generate.mli @@ -29,3 +29,9 @@ val f : -> Javascript.program val init : unit -> unit + +val source_location : + Parse_bytecode.Debug.t + -> ?force:Parse_bytecode.Debug.force + -> Code.loc + -> Javascript.location diff --git a/compiler/lib/link_js.ml b/compiler/lib/link_js.ml index 6b45f29ca..65418e2df 100644 --- a/compiler/lib/link_js.ml +++ b/compiler/lib/link_js.ml @@ -469,7 +469,7 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source let s = sourceMappingURL_base64 ^ Base64.encode_exn data in Line_writer.write oc s | Some file -> - Source_map_io.to_file sm file; + Source_map_io.to_file sm ~file; let s = sourceMappingURL ^ Filename.basename file in Line_writer.write oc s)); if times () then Format.eprintf " sourcemap: %a@." Timer.print t diff --git a/compiler/lib/source_map_io.mli b/compiler/lib/source_map_io.mli index 65c6b905b..aaeccdd6d 100644 --- a/compiler/lib/source_map_io.mli +++ b/compiler/lib/source_map_io.mli @@ -23,6 +23,13 @@ val enabled : bool val to_string : t -> string -val to_file : t -> string -> unit - val of_string : string -> t + +(** Read source map from a file without parsing the mappings (which can be costly). The + [mappings] field is returned empty and the raw string is returned alongside the map. + *) +val of_file_no_mappings : string -> t * string option + +(** Write to a file. If a string is supplied as [mappings], use it instead of the + sourcemap's [mappings]. *) +val to_file : ?mappings:string -> t -> file:string -> unit diff --git a/compiler/lib/source_map_io.yojson.ml b/compiler/lib/source_map_io.yojson.ml index 05f0975e6..7549b4858 100644 --- a/compiler/lib/source_map_io.yojson.ml +++ b/compiler/lib/source_map_io.yojson.ml @@ -19,7 +19,7 @@ open Source_map -let json t = +let json ?replace_mappings t = let rewrite_path path = if Filename.is_relative path then path @@ -38,7 +38,8 @@ let json t = | Some s -> rewrite_path s) ) ; "names", `List (List.map (fun s -> `String s) t.names) ; "sources", `List (List.map (fun s -> `String (rewrite_path s)) t.sources) - ; "mappings", `String (string_of_mapping t.mappings) + ; ( "mappings" + , `String (Option.value ~default:(string_of_mapping t.mappings) replace_mappings) ) ; ( "sourcesContent" , `List (match t.sources_content with @@ -51,7 +52,7 @@ let json t = l) ) ] -let invalid () = invalid_arg "Source_map.of_json" +let invalid () = invalid_arg "Source_map_io.of_json" let string name rest = try @@ -88,34 +89,45 @@ let list_string_opt name rest = | _ -> invalid () with Not_found -> None -let of_json json = - match json with - | `Assoc (("version", `Float version) :: rest) when int_of_float version = 3 -> - let def v d = - match v with - | None -> d - | Some v -> v - in - let file = string "file" rest in - let sourceroot = string "sourceRoot" rest in - let names = list_string "names" rest in - let sources = list_string "sources" rest in - let sources_content = list_string_opt "sourcesContent" rest in - let mappings = string "mappings" rest in - { version = int_of_float version +let of_json ~parse_mappings json = + let parse ~version rest = + let def v d = + match v with + | None -> d + | Some v -> v + in + let file = string "file" rest in + let sourceroot = string "sourceRoot" rest in + let names = list_string "names" rest in + let sources = list_string "sources" rest in + let sources_content = list_string_opt "sourcesContent" rest in + let mappings = string "mappings" rest in + ( { version ; file = def file "" ; sourceroot ; names = def names [] ; sources_content ; sources = def sources [] - ; mappings = mapping_of_string (def mappings "") + ; mappings = if parse_mappings then mapping_of_string (def mappings "") else [] } + , if parse_mappings then None else mappings ) + in + match json with + | `Assoc (("version", `Float version) :: rest) when int_of_float version = 3 -> + parse ~version:3 rest + | `Assoc (("version", `Int 3) :: rest) -> parse ~version:3 rest | _ -> invalid () -let of_string s = of_json (Yojson.Basic.from_string s) +let of_string s = + of_json ~parse_mappings:true (Yojson.Basic.from_string s) |> fst let to_string m = Yojson.Basic.to_string (json m) -let to_file m file = Yojson.Basic.to_file file (json m) +let to_file ?mappings m ~file = + let replace_mappings = mappings in + Yojson.Basic.to_file file (json ?replace_mappings m) + +let of_file_no_mappings filename = + of_json ~parse_mappings:false (Yojson.Basic.from_file filename) let enabled = true diff --git a/compiler/lib/wasm/wa_asm_output.ml b/compiler/lib/wasm/wa_asm_output.ml index 76cb73ad8..cbae054b7 100644 --- a/compiler/lib/wasm/wa_asm_output.ml +++ b/compiler/lib/wasm/wa_asm_output.ml @@ -395,6 +395,9 @@ module Output () = struct | Return_call (x, l) -> Feature.require tail_call; concat_map expression l ^^ line (string "return_call " ^^ index x) + | Location (_, i) -> + (* Source maps not supported for the non-GC target *) + instruction i | ArraySet _ | StructSet _ | Return_call_ref _ -> assert false (* Not supported *) let escape_string s = diff --git a/compiler/lib/wasm/wa_ast.ml b/compiler/lib/wasm/wa_ast.ml index 193e74482..dc039daa3 100644 --- a/compiler/lib/wasm/wa_ast.ml +++ b/compiler/lib/wasm/wa_ast.ml @@ -186,6 +186,8 @@ and instruction = | Return_call_indirect of func_type * expression * expression list | Return_call of var * expression list | Return_call_ref of var * expression * expression list + | Location of Code.loc * instruction + (** Instruction with attached location information *) type import_desc = | Fun of func_type diff --git a/compiler/lib/wasm/wa_code_generation.ml b/compiler/lib/wasm/wa_code_generation.ml index 9fcdc5ec2..37f7e0a7e 100644 --- a/compiler/lib/wasm/wa_code_generation.ml +++ b/compiler/lib/wasm/wa_code_generation.ml @@ -229,6 +229,17 @@ let blk l st = let (), st = l { st with instrs = [] } in List.rev st.instrs, { st with instrs } +let with_location loc instrs st = + let current_instrs = st.instrs in + let (), st = instrs { st with instrs = [] } in + let[@tail_mod_cons] rec add_loc loc = function + | [] -> current_instrs + | W.Nop :: rem -> W.Nop :: add_loc loc rem + | Location _ :: _ as l -> l @ current_instrs (* Stop on the first location *) + | i :: rem -> W.Location (loc, i) :: add_loc loc rem + in + ((), { st with instrs = add_loc loc st.instrs }) + let cast ?(nullable = false) typ e = let* e = e in match typ, e with diff --git a/compiler/lib/wasm/wa_code_generation.mli b/compiler/lib/wasm/wa_code_generation.mli index dec0939dd..17ce241fd 100644 --- a/compiler/lib/wasm/wa_code_generation.mli +++ b/compiler/lib/wasm/wa_code_generation.mli @@ -116,6 +116,8 @@ val is_small_constant : Wa_ast.expression -> bool t val get_i31_value : int -> int option t +val with_location : Code.loc -> unit t -> unit t + type type_def = { supertype : Wa_ast.var option ; final : bool diff --git a/compiler/lib/wasm/wa_generate.ml b/compiler/lib/wasm/wa_generate.ml index 5103209bc..625117147 100644 --- a/compiler/lib/wasm/wa_generate.ml +++ b/compiler/lib/wasm/wa_generate.ml @@ -604,23 +604,30 @@ module Generate (Target : Wa_target_sig.S) = struct | (Not | Lt | Le | Eq | Neq | Ult | Array_get | IsInt | Vectlength), _ -> assert false)) - and translate_instr ctx stack_ctx context (i, _) = - match i with - | Assign (x, y) -> - let* () = assign x (load y) in - Stack.assign stack_ctx x - | Let (x, e) -> - if ctx.live.(Var.idx x) = 0 - then drop (translate_expr ctx stack_ctx context x e) - else store x (translate_expr ctx stack_ctx context x e) - | Set_field (x, n, y) -> Memory.set_field (load x) n (load y) - | Offset_ref (x, n) -> - Memory.set_field - (load x) - 0 - (Value.val_int - Arith.(Value.int_val (Memory.field (load x) 0) + const (Int32.of_int n))) - | Array_set (x, y, z) -> Memory.array_set (load x) (load y) (load z) + and emit_location loc instrs = + match loc with + | No -> instrs + | Before _ | After _ -> + with_location loc instrs + + and translate_instr ctx stack_ctx context (i, loc) = + emit_location loc + (match i with + | Assign (x, y) -> + let* () = assign x (load y) in + Stack.assign stack_ctx x + | Let (x, e) -> + if ctx.live.(Var.idx x) = 0 + then drop (translate_expr ctx stack_ctx context x e) + else store x (translate_expr ctx stack_ctx context x e) + | Set_field (x, n, y) -> Memory.set_field (load x) n (load y) + | Offset_ref (x, n) -> + Memory.set_field + (load x) + 0 + (Value.val_int + Arith.(Value.int_val (Memory.field (load x) 0) + const (Int32.of_int n))) + | Array_set (x, y, z) -> Memory.array_set (load x) (load y) (load z)) and translate_instrs ctx stack_ctx context l = match l with @@ -830,85 +837,87 @@ module Generate (Target : Wa_target_sig.S) = struct let* () = translate_instrs ctx stack_ctx context block.body in let* () = Stack.perform_reloads stack_ctx (`Branch (fst block.branch)) in let* () = Stack.perform_spilling stack_ctx (`Block pc) in - match fst block.branch with - | Branch cont -> - translate_branch result_typ fall_through pc cont context stack_ctx - | Return x -> ( - let* e = load x in - match fall_through with - | `Return -> instr (Push e) - | `Block _ -> instr (Return (Some e))) - | Cond (x, cont1, cont2) -> - let context' = extend_context fall_through context in - if_ - { params = []; result = result_typ } - (Value.check_is_not_zero (load x)) - (translate_branch result_typ fall_through pc cont1 context' stack_ctx) - (translate_branch result_typ fall_through pc cont2 context' stack_ctx) - | Stop -> ( - let* e = Value.unit in - match fall_through with - | `Return -> instr (Push e) - | `Block _ -> instr (Return (Some e))) - | Switch (x, a1, a2) -> - let l = - List.filter - ~f:(fun pc' -> Stack.stack_adjustment_needed stack_ctx ~src:pc ~dst:pc') - (List.rev (Addr.Set.elements (Wa_structure.get_edges dom pc))) - in - let br_table e a context = - let len = Array.length a in - let l = Array.to_list (Array.sub a ~pos:0 ~len:(len - 1)) in - let dest (pc, args) = - assert (List.is_empty args); - label_index context pc + let branch, loc = block.branch in + emit_location loc + (match branch with + | Branch cont -> + translate_branch result_typ fall_through pc cont context stack_ctx + | Return x -> ( + let* e = load x in + match fall_through with + | `Return -> instr (Push e) + | `Block _ -> instr (Return (Some e))) + | Cond (x, cont1, cont2) -> + let context' = extend_context fall_through context in + if_ + { params = []; result = result_typ } + (Value.check_is_not_zero (load x)) + (translate_branch result_typ fall_through pc cont1 context' stack_ctx) + (translate_branch result_typ fall_through pc cont2 context' stack_ctx) + | Stop -> ( + let* e = Value.unit in + match fall_through with + | `Return -> instr (Push e) + | `Block _ -> instr (Return (Some e))) + | Switch (x, a1, a2) -> + let l = + List.filter + ~f:(fun pc' -> Stack.stack_adjustment_needed stack_ctx ~src:pc ~dst:pc') + (List.rev (Addr.Set.elements (Wa_structure.get_edges dom pc))) in - let* e = e in - instr (Br_table (e, List.map ~f:dest l, dest a.(len - 1))) - in - let rec nest l context = - match l with - | pc' :: rem -> - let* () = - Wa_code_generation.block - { params = []; result = [] } - (nest rem (`Block pc' :: context)) - in - let* () = Stack.adjust_stack stack_ctx ~src:pc ~dst:pc' in - instr (Br (label_index context pc', None)) - | [] -> ( - match a1, a2 with - | [||], _ -> br_table (Memory.tag (load x)) a2 context - | _, [||] -> br_table (Value.int_val (load x)) a1 context - | _ -> - (*ZZZ Use Br_on_cast *) - let context' = extend_context fall_through context in - if_ - { params = []; result = result_typ } - (Value.check_is_int (load x)) - (br_table (Value.int_val (load x)) a1 context') - (br_table (Memory.tag (load x)) a2 context')) - in - nest l context - | Raise (x, _) -> - let* e = load x in - let* tag = register_import ~name:exception_name (Tag Value.value) in - instr (Throw (tag, e)) - | Pushtrap (cont, x, cont', _) -> - handle_exceptions - ~result_typ - ~fall_through - ~context:(extend_context fall_through context) - (wrap_with_handlers - p - (fst cont) - (fun ~result_typ ~fall_through ~context -> - translate_branch result_typ fall_through pc cont context stack_ctx)) - x - (fun ~result_typ ~fall_through ~context -> - translate_branch result_typ fall_through pc cont' context stack_ctx) - | Poptrap cont -> - translate_branch result_typ fall_through pc cont context stack_ctx) + let br_table e a context = + let len = Array.length a in + let l = Array.to_list (Array.sub a ~pos:0 ~len:(len - 1)) in + let dest (pc, args) = + assert (List.is_empty args); + label_index context pc + in + let* e = e in + instr (Br_table (e, List.map ~f:dest l, dest a.(len - 1))) + in + let rec nest l context = + match l with + | pc' :: rem -> + let* () = + Wa_code_generation.block + { params = []; result = [] } + (nest rem (`Block pc' :: context)) + in + let* () = Stack.adjust_stack stack_ctx ~src:pc ~dst:pc' in + instr (Br (label_index context pc', None)) + | [] -> ( + match a1, a2 with + | [||], _ -> br_table (Memory.tag (load x)) a2 context + | _, [||] -> br_table (Value.int_val (load x)) a1 context + | _ -> + (*ZZZ Use Br_on_cast *) + let context' = extend_context fall_through context in + if_ + { params = []; result = result_typ } + (Value.check_is_int (load x)) + (br_table (Value.int_val (load x)) a1 context') + (br_table (Memory.tag (load x)) a2 context')) + in + nest l context + | Raise (x, _) -> + let* e = load x in + let* tag = register_import ~name:exception_name (Tag Value.value) in + instr (Throw (tag, e)) + | Pushtrap (cont, x, cont', _) -> + handle_exceptions + ~result_typ + ~fall_through + ~context:(extend_context fall_through context) + (wrap_with_handlers + p + (fst cont) + (fun ~result_typ ~fall_through ~context -> + translate_branch result_typ fall_through pc cont context stack_ctx)) + x + (fun ~result_typ ~fall_through ~context -> + translate_branch result_typ fall_through pc cont' context stack_ctx) + | Poptrap cont -> + translate_branch result_typ fall_through pc cont context stack_ctx)) and translate_branch result_typ fall_through src (dst, args) context stack_ctx = let* () = if List.is_empty args @@ -1109,7 +1118,7 @@ let fix_switch_branches p = p.blocks; !p' -let f ch (p : Code.program) ~live_vars ~in_cps = +let f ch (p : Code.program) ~live_vars ~in_cps ~debug = let p = if Config.Flag.effects () then fix_switch_branches p else p in match target with | `Core -> @@ -1120,5 +1129,5 @@ let f ch (p : Code.program) ~live_vars ~in_cps = | `GC -> let module G = Generate (Wa_gc_target) in let fields, js_code = G.f ~live_vars ~in_cps p in - Wa_wat_output.f ch fields; + Wa_wat_output.f ~debug ch fields; js_code diff --git a/compiler/lib/wasm/wa_generate.mli b/compiler/lib/wasm/wa_generate.mli index a5138ea82..8684e875b 100644 --- a/compiler/lib/wasm/wa_generate.mli +++ b/compiler/lib/wasm/wa_generate.mli @@ -5,4 +5,5 @@ val f : -> Code.program -> live_vars:int array -> in_cps:Effects.in_cps + -> debug:Parse_bytecode.Debug.t -> string list * (string * Javascript.expression) list diff --git a/compiler/lib/wasm/wa_initialize_locals.ml b/compiler/lib/wasm/wa_initialize_locals.ml index 0f0931135..e79f8b3bc 100644 --- a/compiler/lib/wasm/wa_initialize_locals.ml +++ b/compiler/lib/wasm/wa_initialize_locals.ml @@ -88,6 +88,7 @@ and scan_instruction ctx i = | Return_call_indirect (_, e', l) | Return_call_ref (_, e', l) -> scan_expressions ctx l; scan_expression ctx e' + | Location (_, i) -> scan_instruction ctx i and scan_instructions ctx l = let ctx = fork_context ctx in diff --git a/compiler/lib/wasm/wa_tail_call.ml b/compiler/lib/wasm/wa_tail_call.ml index 36cc5466f..b43152f29 100644 --- a/compiler/lib/wasm/wa_tail_call.ml +++ b/compiler/lib/wasm/wa_tail_call.ml @@ -1,5 +1,25 @@ open! Stdlib +let rec get_return ~tail i = + match i with + | Wa_ast.Location (_, i') -> + get_return ~tail i' + | Return (Some (LocalGet y)) -> Some y + | Push (LocalGet y) when tail -> Some y + | _ -> None + +let rec rewrite_tail_call ~y i = + match i with + Wa_ast.Location (loc, i') -> + Option.map ~f:(fun i -> Wa_ast.Location (loc, i)) (rewrite_tail_call ~y i') + | LocalSet (x, Call (symb, l)) when x = y -> + Some ( Return_call (symb, l)) + | LocalSet (x, Call_indirect (ty, e, l)) when x = y -> + Some ( Return_call_indirect (ty, e, l) ) + | LocalSet (x, Call_ref (ty, e, l)) when x = y -> + Some ( Return_call_ref (ty, e, l)) + | _ -> None + let rec instruction ~tail i = match i with | Wa_ast.Loop (ty, l) -> Wa_ast.Loop (ty, instructions ~tail l) @@ -17,6 +37,7 @@ let rec instruction ~tail i = | Push (Call (symb, l)) when tail -> Return_call (symb, l) | Push (Call_indirect (ty, e, l)) when tail -> Return_call_indirect (ty, e, l) | Push (Call_ref (ty, e, l)) when tail -> Return_call_ref (ty, e, l) + | Location (loc, i) -> Location (loc, instruction ~tail i) | Push (Call_ref _) -> i | Drop (BlockExpr (typ, l)) -> Drop (BlockExpr (typ, instructions ~tail:false l)) | Drop _ @@ -43,20 +64,15 @@ and instructions ~tail l = match l with | [] -> [] | [ i ] -> [ instruction ~tail i ] - | [ LocalSet (x, Call (symb, l)); Return (Some (LocalGet y)) ] when x = y -> - [ Return_call (symb, l) ] - | [ LocalSet (x, Call_indirect (ty, e, l)); Return (Some (LocalGet y)) ] when x = y -> - [ Return_call_indirect (ty, e, l) ] - | [ LocalSet (x, Call_ref (ty, e, l)); Return (Some (LocalGet y)) ] when x = y -> - [ Return_call_ref (ty, e, l) ] - | [ LocalSet (x, Call (symb, l)); Push (LocalGet y) ] when tail && x = y -> - [ Return_call (symb, l) ] - | [ LocalSet (x, Call_indirect (ty, e, l)); Push (LocalGet y) ] when tail && x = y -> - [ Return_call_indirect (ty, e, l) ] - | [ LocalSet (x, Call_ref (ty, e, l)); Push (LocalGet y) ] when tail && x = y -> - [ Return_call_ref (ty, e, l) ] | i :: Nop :: rem -> instructions ~tail (i :: rem) | i :: i' :: Nop :: rem -> instructions ~tail (i :: i' :: rem) + | [i; i'] -> + ( match get_return ~tail i' with + None -> [instruction ~tail:false i ; instruction ~tail i'] + | Some y -> + match rewrite_tail_call ~y i with + None -> [instruction ~tail:false i ; instruction ~tail i'] + | Some i'' -> [i'']) | i :: rem -> instruction ~tail:false i :: instructions ~tail rem let f l = instructions ~tail:true l diff --git a/compiler/lib/wasm/wa_wat_output.ml b/compiler/lib/wasm/wa_wat_output.ml index 9c9e07579..aa8b31229 100644 --- a/compiler/lib/wasm/wa_wat_output.ml +++ b/compiler/lib/wasm/wa_wat_output.ml @@ -6,14 +6,22 @@ let target = `Binaryen (*`Reference*) type sexp = | Atom of string | List of sexp list + | Comment of string (** Line comment. String [s] is rendered as [;;s], on its own line, + without space after the double semicolon. *) let rec format_sexp f s = match s with | Atom s -> Format.fprintf f "%s" s | List l -> - Format.fprintf f "@[<2>("; + if List.exists l ~f:(function Comment _ -> true | _ -> false) then + (* Ensure comments are on their own line *) + Format.fprintf f "@[(" + else + Format.fprintf f "@[<2>("; Format.pp_print_list ~pp_sep:(fun f () -> Format.fprintf f "@ ") format_sexp f l; Format.fprintf f ")@]" + | Comment s -> + Format.fprintf f ";;%s" s let index x = Atom ("$" ^ Code.Var.to_string x) @@ -169,6 +177,7 @@ type ctx = ; mutable functions : int Code.Var.Map.t ; mutable function_refs : Code.Var.Set.t ; mutable function_count : int + ; debug : Parse_bytecode.Debug.t } let reference_function ctx f = ctx.function_refs <- Code.Var.Set.add f ctx.function_refs @@ -196,66 +205,66 @@ let expression_or_instructions ctx in_function = let rec expression e = match e with | Const op -> - [ List - [ Atom (type_prefix op "const") - ; Atom - (select - (fun _ i -> Int32.to_string i) - (fun _ i -> Int64.to_string i) - float64 - float32 - op) - ] - ] + [ List + [ Atom (type_prefix op "const") + ; Atom + (select + (fun _ i -> Int32.to_string i) + (fun _ i -> Int64.to_string i) + float64 + float32 + op) + ] + ] | ConstSym (symb, ofs) -> - let i = lookup_symbol ctx symb in - [ List [ Atom "i32.const"; Atom (string_of_int (i + ofs)) ] ] + let i = lookup_symbol ctx symb in + [ List [ Atom "i32.const"; Atom (string_of_int (i + ofs)) ] ] | UnOp (op, e') -> - [ List - (Atom (type_prefix op (select int_un_op int_un_op float_un_op float_un_op op)) - :: expression e') - ] + [ List + (Atom (type_prefix op (select int_un_op int_un_op float_un_op float_un_op op)) + :: expression e') + ] | BinOp (op, e1, e2) -> - [ List - (Atom - (type_prefix - op - (select int_bin_op int_bin_op float_bin_op float_bin_op op)) - :: (expression e1 @ expression e2)) - ] + [ List + (Atom + (type_prefix + op + (select int_bin_op int_bin_op float_bin_op float_bin_op op)) + :: (expression e1 @ expression e2)) + ] | I32WrapI64 e -> [ List (Atom "i32.wrap_i64" :: expression e) ] | I64ExtendI32 (s, e) -> [ List (Atom (signage "i64.extend_i32" s) :: expression e) ] | F32DemoteF64 e -> [ List (Atom "f32.demote_f64" :: expression e) ] | F64PromoteF32 e -> [ List (Atom "f64.promote_f32" :: expression e) ] | Load (offset, e') -> - let offs _ i = - if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] - in - [ List - ((Atom (type_prefix offset "load") :: select offs offs offs offs offset) - @ expression e') - ] + let offs _ i = + if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] + in + [ List + ((Atom (type_prefix offset "load") :: select offs offs offs offs offset) + @ expression e') + ] | Load8 (s, offset, e') -> - let offs _ i = - if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] - in - [ List - (Atom (type_prefix offset (signage "load" s)) - :: select offs offs offs offs offset - @ expression e') - ] + let offs _ i = + if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] + in + [ List + (Atom (type_prefix offset (signage "load" s)) + :: select offs offs offs offs offset + @ expression e') + ] | LocalGet i -> [ List [ Atom "local.get"; Atom (string_of_int i) ] ] | LocalTee (i, e') -> - [ List (Atom "local.tee" :: Atom (string_of_int i) :: expression e') ] + [ List (Atom "local.tee" :: Atom (string_of_int i) :: expression e') ] | GlobalGet nm -> [ List [ Atom "global.get"; symbol nm ] ] | BlockExpr (ty, l) -> [ List (Atom "block" :: (block_type ty @ instructions l)) ] | Call_indirect (typ, e, l) -> - [ List - ((Atom "call_indirect" :: func_type typ) - @ List.concat (List.map ~f:expression (l @ [ e ]))) - ] + [ List + ((Atom "call_indirect" :: func_type typ) + @ List.concat (List.map ~f:expression (l @ [ e ]))) + ] | Call (f, l) -> - [ List (Atom "call" :: index f :: List.concat (List.map ~f:expression l)) ] + [ List (Atom "call" :: index f :: List.concat (List.map ~f:expression l)) ] | MemoryGrow (_, e) -> [ List (Atom "memory.grow" :: expression e) ] | Seq (l, e) -> instructions l @ expression e | Pop ty -> ( @@ -263,178 +272,185 @@ let expression_or_instructions ctx in_function = | `Binaryen -> [ List [ Atom "pop"; value_type ty ] ] | `Reference -> []) | RefFunc symb -> - if in_function then reference_function ctx symb; - [ List [ Atom "ref.func"; index symb ] ] + if in_function then reference_function ctx symb; + [ List [ Atom "ref.func"; index symb ] ] | Call_ref (f, e, l) -> - [ List - (Atom "call_ref" - :: index f - :: List.concat (List.map ~f:expression (l @ [ e ]))) - ] + [ List + (Atom "call_ref" + :: index f + :: List.concat (List.map ~f:expression (l @ [ e ]))) + ] | RefI31 e -> [ List (Atom "ref.i31" :: expression e) ] | I31Get (s, e) -> [ List (Atom (signage "i31.get" s) :: expression e) ] | ArrayNew (typ, e, e') -> - [ List (Atom "array.new" :: index typ :: (expression e @ expression e')) ] + [ List (Atom "array.new" :: index typ :: (expression e @ expression e')) ] | ArrayNewFixed (typ, l) -> - [ List - (Atom "array.new_fixed" - :: index typ - :: Atom (string_of_int (List.length l)) - :: List.concat (List.map ~f:expression l)) - ] + [ List + (Atom "array.new_fixed" + :: index typ + :: Atom (string_of_int (List.length l)) + :: List.concat (List.map ~f:expression l)) + ] | ArrayNewData (typ, data, e, e') -> - [ List - (Atom "array.new_data" - :: index typ - :: index data - :: (expression e @ expression e')) - ] + [ List + (Atom "array.new_data" + :: index typ + :: index data + :: (expression e @ expression e')) + ] | ArrayGet (None, typ, e, e') -> - [ List (Atom "array.get" :: index typ :: (expression e @ expression e')) ] + [ List (Atom "array.get" :: index typ :: (expression e @ expression e')) ] | ArrayGet (Some s, typ, e, e') -> - [ List - (Atom (signage "array.get" s) :: index typ :: (expression e @ expression e')) - ] + [ List + (Atom (signage "array.get" s) :: index typ :: (expression e @ expression e')) + ] | ArrayLen e -> [ List (Atom "array.len" :: expression e) ] | StructNew (typ, l) -> - [ List (Atom "struct.new" :: index typ :: List.concat (List.map ~f:expression l)) - ] + [ List (Atom "struct.new" :: index typ :: List.concat (List.map ~f:expression l)) + ] | StructGet (None, typ, i, e) -> - [ List (Atom "struct.get" :: index typ :: Atom (string_of_int i) :: expression e) - ] + [ List (Atom "struct.get" :: index typ :: Atom (string_of_int i) :: expression e) + ] | StructGet (Some s, typ, i, e) -> - [ List - (Atom (signage "struct.get" s) - :: index typ - :: Atom (string_of_int i) - :: expression e) - ] + [ List + (Atom (signage "struct.get" s) + :: index typ + :: Atom (string_of_int i) + :: expression e) + ] | RefCast (ty, e) -> [ List (Atom "ref.cast" :: ref_type ty :: expression e) ] | RefTest (ty, e) -> [ List (Atom "ref.test" :: ref_type ty :: expression e) ] | RefEq (e, e') -> [ List (Atom "ref.eq" :: (expression e @ expression e')) ] | RefNull ty -> [ List [ Atom "ref.null"; heap_type ty ] ] | Br_on_cast (i, ty, ty', e) -> - [ List - (Atom "br_on_cast" - :: Atom (string_of_int i) - :: ref_type ty - :: ref_type ty' - :: expression e) - ] + [ List + (Atom "br_on_cast" + :: Atom (string_of_int i) + :: ref_type ty + :: ref_type ty' + :: expression e) + ] | Br_on_cast_fail (i, ty, ty', e) -> - [ List - (Atom "br_on_cast_fail" - :: Atom (string_of_int i) - :: ref_type ty - :: ref_type ty' - :: expression e) - ] + [ List + (Atom "br_on_cast_fail" + :: Atom (string_of_int i) + :: ref_type ty + :: ref_type ty' + :: expression e) + ] | ExternInternalize e -> [ List (Atom "extern.internalize" :: expression e) ] | ExternExternalize e -> [ List (Atom "extern.externalize" :: expression e) ] and instruction i = match i with | Drop e -> [ List (Atom "drop" :: expression e) ] | Store (offset, e1, e2) -> - let offs _ i = - if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] - in - [ List - (Atom (type_prefix offset "store") - :: (select offs offs offs offs offset @ expression e1 @ expression e2)) - ] + let offs _ i = + if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] + in + [ List + (Atom (type_prefix offset "store") + :: (select offs offs offs offs offset @ expression e1 @ expression e2)) + ] | Store8 (offset, e1, e2) -> - let offs _ i = - if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] - in - [ List - (Atom (type_prefix offset "store8") - :: (select offs offs offs offs offset @ expression e1 @ expression e2)) - ] + let offs _ i = + if Int32.equal i 0l then [] else [ Atom (Printf.sprintf "offset=%ld" i) ] + in + [ List + (Atom (type_prefix offset "store8") + :: (select offs offs offs offs offset @ expression e1 @ expression e2)) + ] | LocalSet (i, Seq (l, e)) -> instructions (l @ [ LocalSet (i, e) ]) | LocalSet (i, e) -> - [ List (Atom "local.set" :: Atom (string_of_int i) :: expression e) ] + [ List (Atom "local.set" :: Atom (string_of_int i) :: expression e) ] | GlobalSet (nm, e) -> [ List (Atom "global.set" :: symbol nm :: expression e) ] | Loop (ty, l) -> [ List (Atom "loop" :: (block_type ty @ instructions l)) ] | Block (ty, l) -> [ List (Atom "block" :: (block_type ty @ instructions l)) ] | If (ty, e, l1, l2) -> - [ List - (Atom "if" - :: (block_type ty + [ List + (Atom "if" + :: (block_type ty @ expression e @ list ~always:true "then" instructions (remove_nops l1) @ list "else" instructions (remove_nops l2))) - ] + ] | Try (ty, body, catches, catch_all) -> - [ List - (Atom "try" - :: (block_type ty + [ List + (Atom "try" + :: (block_type ty @ List (Atom "do" :: instructions body) :: (List.map ~f:(fun (tag, l) -> List (Atom "catch" :: index tag :: instructions l)) catches - @ - match catch_all with - | None -> [] - | Some l -> [ List (Atom "catch_all" :: instructions l) ]))) - ] + @ + match catch_all with + | None -> [] + | Some l -> [ List (Atom "catch_all" :: instructions l) ]))) + ] | Br_table (e, l, i) -> - [ List - (Atom "br_table" - :: (List.map ~f:(fun i -> Atom (string_of_int i)) (l @ [ i ]) @ expression e) - ) - ] + [ List + (Atom "br_table" + :: (List.map ~f:(fun i -> Atom (string_of_int i)) (l @ [ i ]) @ expression e) + ) + ] | Br (i, e) -> - [ List - (Atom "br" - :: Atom (string_of_int i) - :: - (match e with + [ List + (Atom "br" + :: Atom (string_of_int i) + :: + (match e with | None -> [] | Some e -> expression e)) - ] + ] | Br_if (i, e) -> [ List (Atom "br_if" :: Atom (string_of_int i) :: expression e) ] | Return e -> - [ List - (Atom "return" - :: - (match e with + [ List + (Atom "return" + :: + (match e with | None -> [] | Some e -> expression e)) - ] + ] | Throw (tag, e) -> [ List (Atom "throw" :: index tag :: expression e) ] | Rethrow i -> [ List [ Atom "rethrow"; Atom (string_of_int i) ] ] | CallInstr (f, l) -> - [ List (Atom "call" :: index f :: List.concat (List.map ~f:expression l)) ] + [ List (Atom "call" :: index f :: List.concat (List.map ~f:expression l)) ] | Nop -> [] | Push e -> expression e | ArraySet (typ, e, e', e'') -> - [ List - (Atom "array.set" - :: index typ - :: (expression e @ expression e' @ expression e'')) - ] + [ List + (Atom "array.set" + :: index typ + :: (expression e @ expression e' @ expression e'')) + ] | StructSet (typ, i, e, e') -> - [ List - (Atom "struct.set" - :: index typ - :: Atom (string_of_int i) - :: (expression e @ expression e')) - ] + [ List + (Atom "struct.set" + :: index typ + :: Atom (string_of_int i) + :: (expression e @ expression e')) + ] | Return_call_indirect (typ, e, l) -> - [ List - ((Atom "return_call_indirect" :: func_type typ) - @ List.concat (List.map ~f:expression (l @ [ e ]))) - ] + [ List + ((Atom "return_call_indirect" :: func_type typ) + @ List.concat (List.map ~f:expression (l @ [ e ]))) + ] | Return_call (f, l) -> - [ List (Atom "return_call" :: index f :: List.concat (List.map ~f:expression l)) ] + [ List (Atom "return_call" :: index f :: List.concat (List.map ~f:expression l)) ] | Return_call_ref (typ, e, l) -> - [ List - (Atom "return_call_ref" - :: index typ - :: List.concat (List.map ~f:expression (l @ [ e ]))) - ] - and instructions l = List.concat (List.map ~f:instruction l) in + [ List + (Atom "return_call_ref" + :: index typ + :: List.concat (List.map ~f:expression (l @ [ e ]))) + ] + | Location (loc, i) -> + let loc = Generate.source_location ctx.debug loc in + (match loc with + | Javascript.N | U | Pi Parse_info.{ src = None; _ } -> instruction i + | Pi Parse_info.{ src = Some src; col; line; _ } -> + let loc = Format.sprintf "%s:%d:%d" src line col in + Comment ("@ " ^ loc) :: instruction i) +and instructions l = List.concat (List.map ~f:instruction l) in expression, instructions let expression ctx = fst (expression_or_instructions ctx false) @@ -553,13 +569,14 @@ let data_offsets fields = ~init:(0, Code.Var.Map.empty) fields -let f ch fields = +let f ~debug ch fields = let heap_base, addresses = data_offsets fields in let ctx = { addresses ; functions = Code.Var.Map.empty ; function_refs = Code.Var.Set.empty ; function_count = 0 + ; debug } in let other_fields = List.concat (List.map ~f:(fun f -> field ctx f) fields) in diff --git a/compiler/lib/wasm/wa_wat_output.mli b/compiler/lib/wasm/wa_wat_output.mli index 59f2b93d9..1f0c48477 100644 --- a/compiler/lib/wasm/wa_wat_output.mli +++ b/compiler/lib/wasm/wa_wat_output.mli @@ -1 +1 @@ -val f : out_channel -> Wa_ast.module_field list -> unit +val f : debug: Parse_bytecode.Debug.t -> out_channel -> Wa_ast.module_field list -> unit