aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2022-05-20 21:56:34 +0200
committerLuuk de Gram <luuk@degram.dev>2022-06-24 08:12:17 +0200
commit359b61aec3494197aaca336dabaa39d0515706ff (patch)
treeded082ba135b33f2b691d9c615aeae6e943e8e28 /src
parent291c08f7b0ea4e333c37a0ac378176891f255fa0 (diff)
downloadzig-359b61aec3494197aaca336dabaa39d0515706ff.tar.gz
zig-359b61aec3494197aaca336dabaa39d0515706ff.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/arch/wasm/CodeGen.zig84
-rw-r--r--src/link/Wasm.zig32
2 files changed, 106 insertions, 10 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index f2710574d7..28450c19c2 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -795,7 +795,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.
var returns = std.ArrayList(wasm.Valtype).init(gpa);
defer returns.deinit();
- if (firstParamSRet(fn_info, target)) {
+ if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) {
try params.append(.i32); // memory address is always a 32-bit handle
} else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {
if (fn_info.cc == .C) {
@@ -993,7 +993,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
// Check if we store the result as a pointer to the stack rather than
// by value
- if (firstParamSRet(fn_ty.fnInfo(), self.target)) {
+ const fn_info = fn_ty.fnInfo();
+ if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
// the sret arg will be passed as first argument, therefore we
// set the `return_value` before allocating locals for regular args.
result.return_value = .{ .local = self.local_index };
@@ -1027,11 +1028,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
return result;
}
-fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
- switch (fn_info.cc) {
- .Unspecified, .Inline => return isByRef(fn_info.return_type, target),
+fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target: std.Target) bool {
+ switch (cc) {
+ .Unspecified, .Inline => return isByRef(return_type, target),
.C => {
- const ty_classes = abi.classifyType(fn_info.return_type, target);
+ const ty_classes = abi.classifyType(return_type, target);
if (ty_classes[0] == .indirect) return true;
if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
return false;
@@ -1678,7 +1679,8 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
return self.allocStack(Type.usize); // create pointer to void
}
- if (firstParamSRet(self.decl.ty.fnInfo(), self.target)) {
+ const fn_info = self.decl.ty.fnInfo();
+ if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
return self.return_value;
}
@@ -1697,7 +1699,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
}
}
- if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) {
+ const fn_info = self.decl.ty.fnInfo();
+ if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
const result = try self.load(operand, ret_ty, 0);
try self.emitWValue(result);
}
@@ -1720,7 +1723,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
else => unreachable,
};
const ret_ty = fn_ty.fnReturnType();
- const first_param_sret = firstParamSRet(fn_ty.fnInfo(), self.target);
+ const fn_info = fn_ty.fnInfo();
+ const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, self.target);
const callee: ?*Decl = blk: {
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 {
return shift_result;
}
}
+
+/// Calls a compiler-rt intrinsic by creating an undefined symbol,
+/// then lowering the arguments and calling the symbol as a function call.
+/// This function call assumes the C-ABI.
+fn callIntrinsic(
+ self: *Self,
+ name: []const u8,
+ param_types: []const Type,
+ return_type: Type,
+ args: []const WValue,
+) InnerError!WValue {
+ assert(param_types.len == args.len);
+ const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name));
+ var pt_tmp = try self.gpa.dupe(Type, param_types);
+ defer self.gpa.free(pt_tmp);
+
+ // TODO: have genFunctype accept individual params so we don't,
+ // need to initialize a fake Fn.Data instance.
+ const func_type = try genFunctype(self.base.allocator, .{
+ .param_types = pt_tmp,
+ .comptime_params = undefined,
+ .return_type = return_type,
+ .alignment = 0,
+ .cc = .C,
+ .is_var_args = false,
+ .is_generic = false,
+ }, self.target);
+ defer func_type.deinit(self.base.allocator);
+ const func_type_index = try self.bin_file.putOrGetFuncType(func_type);
+ try self.bin_file.addOrUpdateImport(symbol_index, func_type_index);
+
+ const want_sret_param = firstParamSRet(.C, return_type, self.target);
+ // if we want return as first param, we allocate a pointer to stack,
+ // and emit it as our first argument
+ const sret = if (want_sret_param) blk: {
+ const sret_local = try self.allocStack(return_type);
+ try self.lowerToStack(sret_local);
+ break :blk sret_local;
+ } else WValue{ .none = {} };
+
+ // Lower all arguments to the stack before we call our function
+ for (args) |arg, arg_i| {
+ assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime());
+ try self.lowerArg(.C, param_types[arg_i], arg);
+ }
+
+ // Actually call our intrinsic
+ try self.addLabel(.call, symbol_index);
+
+ if (!return_type.hasRuntimeBitsIgnoreComptime()) {
+ return WValue.none;
+ } else if (return_type.isNoReturn()) {
+ try self.addTag(.@"unreachable");
+ return WValue.none;
+ } else if (want_sret_param) {
+ return sret;
+ } else {
+ const result_local = try self.allocLocal(return_type);
+ try self.addLabel(.local_set, result_local.local);
+ return result_local;
+ }
+}
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index cff47ccdf5..93240623ca 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -406,7 +406,6 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
continue;
}
- // TODO: locals are allowed to have duplicate symbol names
// TODO: Store undefined symbols so we can verify at the end if they've all been found
// if not, emit an error (unless --allow-undefined is enabled).
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
return atom.sym_index;
}
+/// Returns the symbol index from the name of an intrinsic.
+/// If the symbol does not yet exist, creates a new one symbol instead
+/// and then returns the index to it.
+pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 {
+ const name_index = try self.string_table.put(self.base.allocator, name);
+ const gop = try self.globals.getOrPut(self.base.allocator, name_index);
+ if (gop.found_existing) {
+ return gop.value_ptr.*.index;
+ }
+
+ var symbol: Symbol = .{
+ .name = name_index,
+ .flags = 0,
+ .index = undefined, // index to type will be set after merging function symbols
+ .tag = .function,
+ };
+ symbol.setGlobal(true);
+ symbol.setFlag(.WASM_SYM_UNDEFINED);
+
+ const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: {
+ var index = @intCast(u32, self.symbols.items.len);
+ try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
+ self.symbols.items.len += 1;
+ break :blk index;
+ };
+ self.symbols.items[sym_index] = symbol;
+ gop.value_ptr.* = .{ .index = sym_index, .file = null };
+
+ return sym_index;
+}
+
/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
/// Returns the given pointer address
pub fn getDeclVAddr(