authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-20 21:56:34+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-24 08:12:17+02:00
log359b61aec3494197aaca336dabaa39d0515706ff
treeded082ba135b33f2b691d9c615aeae6e943e8e28
parent291c08f7b0ea4e333c37a0ac378176891f255fa0

wasm: Create compiler-rt symbols and lowering

Implements the creation of an undefined symbol for a compiler-rt intrinsic. Also implements the building of the function call to said compiler-rt intrinsic.

2 files changed, 106 insertions(+), 10 deletions(-)

src/arch/wasm/CodeGen.zig+75-9
...@@ -795,7 +795,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std....@@ -795,7 +795,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.
795 var returns = std.ArrayList(wasm.Valtype).init(gpa);795 var returns = std.ArrayList(wasm.Valtype).init(gpa);
796 defer returns.deinit();796 defer returns.deinit();
797797
798 if (firstParamSRet(fn_info, target)) {798 if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) {
799 try params.append(.i32); // memory address is always a 32-bit handle799 try params.append(.i32); // memory address is always a 32-bit handle
800 } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {800 } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {
801 if (fn_info.cc == .C) {801 if (fn_info.cc == .C) {
...@@ -993,7 +993,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -993,7 +993,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
993993
994 // Check if we store the result as a pointer to the stack rather than994 // Check if we store the result as a pointer to the stack rather than
995 // by value995 // by value
996 if (firstParamSRet(fn_ty.fnInfo(), self.target)) {996 const fn_info = fn_ty.fnInfo();
997 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
997 // the sret arg will be passed as first argument, therefore we998 // the sret arg will be passed as first argument, therefore we
998 // set the `return_value` before allocating locals for regular args.999 // set the `return_value` before allocating locals for regular args.
999 result.return_value = .{ .local = self.local_index };1000 result.return_value = .{ .local = self.local_index };
...@@ -1027,11 +1028,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -1027,11 +1028,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
1027 return result;1028 return result;
1028}1029}
10291030
1030fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {1031fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target: std.Target) bool {
1031 switch (fn_info.cc) {1032 switch (cc) {
1032 .Unspecified, .Inline => return isByRef(fn_info.return_type, target),1033 .Unspecified, .Inline => return isByRef(return_type, target),
1033 .C => {1034 .C => {
1034 const ty_classes = abi.classifyType(fn_info.return_type, target);1035 const ty_classes = abi.classifyType(return_type, target);
1035 if (ty_classes[0] == .indirect) return true;1036 if (ty_classes[0] == .indirect) return true;
1036 if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;1037 if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
1037 return false;1038 return false;
...@@ -1678,7 +1679,8 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1678,7 +1679,8 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1678 return self.allocStack(Type.usize); // create pointer to void1679 return self.allocStack(Type.usize); // create pointer to void
1679 }1680 }
16801681
1681 if (firstParamSRet(self.decl.ty.fnInfo(), self.target)) {1682 const fn_info = self.decl.ty.fnInfo();
1683 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1682 return self.return_value;1684 return self.return_value;
1683 }1685 }
16841686
...@@ -1697,7 +1699,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1697,7 +1699,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1697 }1699 }
1698 }1700 }
16991701
1700 if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) {1702 const fn_info = self.decl.ty.fnInfo();
1703 if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1701 const result = try self.load(operand, ret_ty, 0);1704 const result = try self.load(operand, ret_ty, 0);
1702 try self.emitWValue(result);1705 try self.emitWValue(result);
1703 }1706 }
...@@ -1720,7 +1723,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -1720,7 +1723,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
1720 else => unreachable,1723 else => unreachable,
1721 };1724 };
1722 const ret_ty = fn_ty.fnReturnType();1725 const ret_ty = fn_ty.fnReturnType();
1723 const first_param_sret = firstParamSRet(fn_ty.fnInfo(), self.target);1726 const fn_info = fn_ty.fnInfo();
1727 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, self.target);
17241728
1725 const callee: ?*Decl = blk: {1729 const callee: ?*Decl = blk: {
1726 const func_val = self.air.value(pl_op.operand) orelse break :blk null;1730 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
...@@ -5091,3 +5095,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5091,3 +5095,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5091 return shift_result;5095 return shift_result;
5092 }5096 }
5093}5097}
5098
5099/// Calls a compiler-rt intrinsic by creating an undefined symbol,
5100/// then lowering the arguments and calling the symbol as a function call.
5101/// This function call assumes the C-ABI.
5102fn callIntrinsic(
5103 self: *Self,
5104 name: []const u8,
5105 param_types: []const Type,
5106 return_type: Type,
5107 args: []const WValue,
5108) InnerError!WValue {
5109 assert(param_types.len == args.len);
5110 const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name));
5111 var pt_tmp = try self.gpa.dupe(Type, param_types);
5112 defer self.gpa.free(pt_tmp);
5113
5114 // TODO: have genFunctype accept individual params so we don't,
5115 // need to initialize a fake Fn.Data instance.
5116 const func_type = try genFunctype(self.base.allocator, .{
5117 .param_types = pt_tmp,
5118 .comptime_params = undefined,
5119 .return_type = return_type,
5120 .alignment = 0,
5121 .cc = .C,
5122 .is_var_args = false,
5123 .is_generic = false,
5124 }, self.target);
5125 defer func_type.deinit(self.base.allocator);
5126 const func_type_index = try self.bin_file.putOrGetFuncType(func_type);
5127 try self.bin_file.addOrUpdateImport(symbol_index, func_type_index);
5128
5129 const want_sret_param = firstParamSRet(.C, return_type, self.target);
5130 // if we want return as first param, we allocate a pointer to stack,
5131 // and emit it as our first argument
5132 const sret = if (want_sret_param) blk: {
5133 const sret_local = try self.allocStack(return_type);
5134 try self.lowerToStack(sret_local);
5135 break :blk sret_local;
5136 } else WValue{ .none = {} };
5137
5138 // Lower all arguments to the stack before we call our function
5139 for (args) |arg, arg_i| {
5140 assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime());
5141 try self.lowerArg(.C, param_types[arg_i], arg);
5142 }
5143
5144 // Actually call our intrinsic
5145 try self.addLabel(.call, symbol_index);
5146
5147 if (!return_type.hasRuntimeBitsIgnoreComptime()) {
5148 return WValue.none;
5149 } else if (return_type.isNoReturn()) {
5150 try self.addTag(.@"unreachable");
5151 return WValue.none;
5152 } else if (want_sret_param) {
5153 return sret;
5154 } else {
5155 const result_local = try self.allocLocal(return_type);
5156 try self.addLabel(.local_set, result_local.local);
5157 return result_local;
5158 }
5159}
src/link/Wasm.zig+31-1
...@@ -406,7 +406,6 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -406,7 +406,6 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
406 continue;406 continue;
407 }407 }
408408
409 // TODO: locals are allowed to have duplicate symbol names
410 // TODO: Store undefined symbols so we can verify at the end if they've all been found409 // TODO: Store undefined symbols so we can verify at the end if they've all been found
411 // if not, emit an error (unless --allow-undefined is enabled).410 // if not, emit an error (unless --allow-undefined is enabled).
412 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index);411 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index);
...@@ -753,6 +752,37 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -753,6 +752,37 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
753 return atom.sym_index;752 return atom.sym_index;
754}753}
755754
755/// Returns the symbol index from the name of an intrinsic.
756/// If the symbol does not yet exist, creates a new one symbol instead
757/// and then returns the index to it.
758pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 {
759 const name_index = try self.string_table.put(self.base.allocator, name);
760 const gop = try self.globals.getOrPut(self.base.allocator, name_index);
761 if (gop.found_existing) {
762 return gop.value_ptr.*.index;
763 }
764
765 var symbol: Symbol = .{
766 .name = name_index,
767 .flags = 0,
768 .index = undefined, // index to type will be set after merging function symbols
769 .tag = .function,
770 };
771 symbol.setGlobal(true);
772 symbol.setFlag(.WASM_SYM_UNDEFINED);
773
774 const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: {
775 var index = @intCast(u32, self.symbols.items.len);
776 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
777 self.symbols.items.len += 1;
778 break :blk index;
779 };
780 self.symbols.items[sym_index] = symbol;
781 gop.value_ptr.* = .{ .index = sym_index, .file = null };
782
783 return sym_index;
784}
785
756/// For a given decl, find the given symbol index's atom, and create a relocation for the type.786/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
757/// Returns the given pointer address787/// Returns the given pointer address
758pub fn getDeclVAddr(788pub fn getDeclVAddr(