Skip to content

Commit

Permalink
feat(compiler): Allow function re-exports to use regular call instruc…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
ospencer committed Apr 21, 2022
1 parent b2d7440 commit 0f96764
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 55 deletions.
66 changes: 32 additions & 34 deletions compiler/src/codegen/compcore.re
Original file line number Diff line number Diff line change
Expand Up @@ -3393,53 +3393,51 @@ let compile_imports = (wasm_mod, env, {imports}) => {
);
};

let compile_exports = (wasm_mod, env, {functions, imports, exports, globals}) => {
let compile_export = (i, {ex_name, ex_global_index}) => {
let internal_name = Printf.sprintf("global_%ld", ex_global_index);
let exported_name = "GRAIN$EXPORT$" ++ Ident.name(ex_name);
ignore @@ Export.add_global_export(wasm_mod, internal_name, exported_name);
};

let compile_external_function_export = ((internal_name, external_name)) => {
ignore @@
Export.add_function_export(wasm_mod, internal_name, external_name);
let compile_exports = (wasm_mod, env, {imports, exports, globals}) => {
let compile_export = (i, export) => {
switch (export) {
| GlobalExport({ex_global_name, ex_global_index}) =>
let internal_name = Printf.sprintf("global_%ld", ex_global_index);
let exported_name = "GRAIN$EXPORT$" ++ Ident.name(ex_global_name);
ignore @@
Export.add_global_export(wasm_mod, internal_name, exported_name);
| FunctionExport({ex_function_internal_name, ex_function_name}) =>
ignore @@
Export.add_function_export(
wasm_mod,
ex_function_internal_name,
ex_function_name,
)
};
};

let exports = {
let exported = Hashtbl.create(14);
module StringSet = Set.Make(String);
let exported_globals = ref(StringSet.empty);
let exported_functions = ref(StringSet.empty);
/* Exports are already reversed, so keeping the first of any name is the correct behavior. */
List.filter(
({ex_name}) =>
if (Hashtbl.mem(exported, Ident.name(ex_name))) {
fun
| GlobalExport({ex_global_name}) =>
if (StringSet.mem(Ident.name(ex_global_name), exported_globals^)) {
false;
} else {
Hashtbl.add(exported, Ident.name(ex_name), ());
exported_globals :=
StringSet.add(Ident.name(ex_global_name), exported_globals^);
true;
}
| FunctionExport({ex_function_name}) =>
if (StringSet.mem(ex_function_name, exported_functions^)) {
false;
} else {
exported_functions :=
StringSet.add(ex_function_name, exported_functions^);
true;
},
exports,
);
};
let functions = {
let exported = Hashtbl.create(14);
/* Functions will be reversed, so keeping the first of any name is the correct behavior. */
List.filter_map(
({index, name, id}) =>
switch (name) {
| Some(name) =>
if (Hashtbl.mem(exported, name)) {
None;
} else {
Hashtbl.add(exported, name, ());
let internal_name = Ident.unique_name(id);
Some((internal_name, name));
}
| None => None
},
functions,
);
};
List.iteri(compile_export, exports);
List.iter(compile_external_function_export, List.rev(functions));
ignore @@ Export.add_function_export(wasm_mod, grain_main, grain_main);
ignore @@ Export.add_function_export(wasm_mod, grain_start, grain_start);
ignore @@
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/codegen/mashtree.re
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,15 @@ type import = {
};

[@deriving sexp]
type export = {
ex_name: Ident.t,
ex_global_index: int32,
};
type export =
| FunctionExport({
ex_function_name: string,
ex_function_internal_name: string,
})
| GlobalExport({
ex_global_name: Ident.t,
ex_global_index: int32,
});

[@deriving sexp]
type mash_function = {
Expand Down
76 changes: 59 additions & 17 deletions compiler/src/codegen/transl_anf.re
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ let global_index = ref(0);
let global_exports = () => {
let tbl = global_table^;
Ident.fold_all(
(ex_name, (exported, ex_global_index, _), acc) =>
(ex_global_name, (exported, ex_global_index, _), acc) =>
if (exported) {
[{ex_name, ex_global_index}, ...acc];
[GlobalExport({ex_global_name, ex_global_index}), ...acc];
} else {
acc;
},
Expand Down Expand Up @@ -511,6 +511,12 @@ let run_register_allocation = (instrs: list(Mashtree.instr)) => {
|> run(Types.StackAllocated(WasmF64));
};

let grain_import_name = (mod_, name) =>
Printf.sprintf("gimport_%s_%s", mod_, name);

let wasm_import_name = (mod_, name) =>
Printf.sprintf("wimport_%s_%s", mod_, name);

let compile_const = (c: Asttypes.constant) =>
switch (c) {
| Const_number(Const_number_int(i)) => MConstI32(Int64.to_int32(i))
Expand Down Expand Up @@ -565,6 +571,7 @@ let known_function = f =>
let compile_lambda =
(~name=?, id, env, args, body, attrs, loc): Mashtree.closure_data => {
register_function(Internal(id));

let (body, return_type) = body;
// NOTE: we special-case `id`, since we want to
// have simply-recursive uses of identifiers use
Expand Down Expand Up @@ -654,6 +661,7 @@ let compile_lambda =
let compile_wrapper =
(~export_name=?, id, env, func_name, args, rets): Mashtree.closure_data => {
register_function(Internal(id));

let body = [
{
instr_desc:
Expand Down Expand Up @@ -1058,7 +1066,7 @@ let lift_imports = (env, imports) => {
| GrainValue(mod_, name) =>
let mimp_mod = Ident.create_persistent(mod_);
let mimp_name = Ident.create_persistent(name);
let import_name = Printf.sprintf("gimport_%s_%s", mod_, name);
let import_name = grain_import_name(mod_, name);
let (alloc, mods) =
switch (imp_shape) {
| GlobalShape(alloc) => (
Expand Down Expand Up @@ -1137,8 +1145,7 @@ let lift_imports = (env, imports) => {
Ident.add(
imp_use_id,
MGlobalBind(
Printf.sprintf(
"wimport_%s_%s",
wasm_import_name(
Ident.name(mimp_mod),
Ident.name(mimp_name),
),
Expand All @@ -1160,7 +1167,7 @@ let lift_imports = (env, imports) => {
mimp_setup: MWrap(Int32.zero),
mimp_used: true,
};
let func_name = Printf.sprintf("wimport_%s_%s", mod_, name);
let func_name = wasm_import_name(mod_, name);
let export_name =
if (exported) {
Some(name);
Expand Down Expand Up @@ -1228,44 +1235,78 @@ let lift_imports = (env, imports) => {
(imports, setups, env);
};

let transl_signature = (functions, signature) => {
let transl_signature = (functions, imports, signature) => {
open Types;

let function_exports = ref([]);

// At this point in compilation, we know which functions can be called
// directly/indirectly at the wasm level. We add this information to the
// module signature.
let func_map = Ident_tbl.create(30);
List.iter(
func => Ident_tbl.add(func_map, (func: mash_function).id, func),
(func: mash_function) =>
switch (func.name) {
| Some(name) =>
Ident_tbl.add(func_map, func.id, Ident.unique_name(func.id))
| None => ()
},
functions,
);
List.iter(
imp =>
switch (imp.imp_shape) {
| FunctionShape(_) =>
let internal_name =
switch (imp.imp_desc) {
| GrainValue(mod_, name) => grain_import_name(mod_, name)
| WasmFunction(mod_, name) => Ident.unique_name(imp.imp_use_id)
| _ => failwith("Wasm or js value had FunctionShape")
};
Ident_tbl.add(func_map, imp.imp_use_id, internal_name);
| _ => ()
},
imports,
);
let sign =
List.map(
fun
| TSigValue(
_,
vid,
{
val_repr: ReprFunction(args, rets, _),
val_fullpath: Path.PIdent(id),
} as vd,
) => {
switch (Ident_tbl.find_opt(func_map, id)) {
| Some({name: Some(name)}) =>
| Some(internal_name) =>
let external_name = Ident.name(vid);
function_exports :=
[
FunctionExport({
ex_function_name: external_name,
ex_function_internal_name: internal_name,
}),
...function_exports^,
];
TSigValue(
id,
{...vd, val_repr: ReprFunction(args, rets, Direct(name))},
)
vid,
{
...vd,
val_repr: ReprFunction(args, rets, Direct(external_name)),
},
);
| _ =>
TSigValue(
id,
vid,
{...vd, val_repr: ReprFunction(args, rets, Indirect)},
)
};
}
| _ as item => item,
signature.Cmi_format.cmi_sign,
);
{...signature, cmi_sign: sign};
({...signature, cmi_sign: sign}, function_exports^);
};

let transl_anf_program =
Expand Down Expand Up @@ -1297,14 +1338,15 @@ let transl_anf_program =
};
let main_body =
run_register_allocation @@ setups @ compile_anf_expr(env, anf_prog.body);
let exports = global_exports();
let functions =
List.map(
({body} as f: Mashtree.mash_function) =>
{...f, body: run_register_allocation(body)},
compile_remaining_worklist(),
);
let signature = transl_signature(functions, anf_prog.signature);
let (signature, function_exports) =
transl_signature(functions, anf_prog.imports, anf_prog.signature);
let exports = function_exports @ global_exports();

{
functions,
Expand Down
24 changes: 24 additions & 0 deletions compiler/src/middle_end/optimize_constants.re
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ module ConstantPropagationArg: Anf_mapper.MapArgument = {
}
| _ => i
};

let leave_anf_program = ({signature} as p) => {
// Support directly exporting imports
let cmi_sign =
List.map(
fun
| TSigValue(vid, {val_fullpath: Path.PIdent(id)} as vd) as item => {
switch (Ident.find_same_opt(id, known_constants^)) {
| Some(ImmId(immid)) =>
TSigValue(vid, {...vd, val_fullpath: Path.PIdent(immid)})
| _ => item
};
}
| item => item,
signature.cmi_sign,
);
{
...p,
signature: {
...signature,
cmi_sign,
},
};
};
};

module ConstantPropagationMapper = Anf_mapper.MakeMap(ConstantPropagationArg);
Expand Down

0 comments on commit 0f96764

Please sign in to comment.