diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 92a292116e..49754f03f6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3075,7 +3075,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue .decl => |decl_index| { return func.lowerParentPtrDecl(ptr_val, decl_index, offset); }, - .anon_decl => @panic("TODO"), + .anon_decl => |ad| return func.lowerAnonDeclRef(ad, offset), .mut_decl => |mut_decl| { const decl_index = mut_decl.decl; return func.lowerParentPtrDecl(ptr_val, decl_index, offset); @@ -3139,6 +3139,32 @@ fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.In return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index, offset); } +fn lowerAnonDeclRef(func: *CodeGen, anon_decl: InternPool.Index, offset: u32) InnerError!WValue { + const mod = func.bin_file.base.options.module.?; + const ty = mod.intern_pool.typeOf(anon_decl).toType(); + + const is_fn_body = ty.zigTypeTag(mod) == .Fn; + if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(mod)) { + return WValue{ .imm32 = 0xaaaaaaaa }; + } + + const res = try func.bin_file.lowerAnonDecl(anon_decl, func.decl.srcLoc(mod)); + switch (res) { + .ok => {}, + .fail => |em| { + func.err_msg = em; + return error.CodegenFail; + }, + } + const target_atom_index = func.bin_file.anon_decls.get(anon_decl).?; + const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?; + if (is_fn_body) { + return WValue{ .function_index = target_sym_index }; + } else if (offset == 0) { + return WValue{ .memory = target_sym_index }; + } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } }; +} + fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Index, offset: u32) InnerError!WValue { const mod = func.bin_file.base.options.module.?; if (tv.ty.isSlice(mod)) { @@ -3306,6 +3332,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { .mut_decl => |mut_decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, mut_decl.decl, 0), .int => |int| return func.lowerConstant(int.toValue(), ip.typeOf(int).toType()), .opt_payload, .elem, .field => return func.lowerParentPtr(val, 0), + .anon_decl => |ad| return func.lowerAnonDeclRef(ad, 0), else => return func.fail("Wasm TODO: lowerConstant for other const addr tag {}", .{ptr.addr}), }, .opt => if (ty.optionalReprIsPayload(mod)) { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 34244d2247..2116cd7708 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -187,6 +187,9 @@ debug_pubtypes_atom: ?Atom.Index = null, /// rather than by the linker. synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{}, +/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. +anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{}, + pub const Alignment = types.Alignment; pub const Segment = struct { @@ -1291,6 +1294,7 @@ pub fn deinit(wasm: *Wasm) void { } wasm.decls.deinit(gpa); + wasm.anon_decls.deinit(gpa); wasm.atom_types.deinit(gpa); wasm.symbols.deinit(gpa); wasm.symbols_free_list.deinit(gpa); @@ -1548,17 +1552,38 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions const decl = mod.declPtr(decl_index); - // Create and initialize a new local symbol and atom - const atom_index = try wasm.createAtom(); const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); - const parent_atom = wasm.getAtomPtr(parent_atom_index); + const parent_atom = wasm.getAtom(parent_atom_index); const local_index = parent_atom.locals.items.len; - try parent_atom.locals.append(wasm.base.allocator, atom_index); const fqn = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqn, local_index, }); defer wasm.base.allocator.free(name); + + switch (try wasm.lowerConst(name, tv, decl.srcLoc(mod))) { + .ok => |atom_index| { + try wasm.getAtomPtr(parent_atom_index).locals.append(wasm.base.allocator, atom_index); + return wasm.getAtom(atom_index).getSymbolIndex().?; + }, + .fail => |em| { + decl.analysis = .codegen_failure; + try mod.failed_decls.put(mod.gpa, decl_index, em); + return error.CodegenFail; + }, + } +} + +const LowerConstResult = union(enum) { + ok: Atom.Index, + fail: *Module.ErrorMsg, +}; + +fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult { + const mod = wasm.base.options.module.?; + + // Create and initialize a new local symbol and atom + const atom_index = try wasm.createAtom(); var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); defer value_bytes.deinit(); @@ -1576,7 +1601,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In const result = try codegen.generateSymbol( &wasm.base, - decl.srcLoc(mod), + src_loc, tv, &value_bytes, .none, @@ -1588,17 +1613,15 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In break :code switch (result) { .ok => value_bytes.items, .fail => |em| { - decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, em); - return error.CodegenFail; + return .{ .fail = em }; }, }; }; const atom = wasm.getAtomPtr(atom_index); - atom.size = @as(u32, @intCast(code.len)); + atom.size = @intCast(code.len); try atom.code.appendSlice(wasm.base.allocator, code); - return atom.sym_index; + return .{ .ok = atom_index }; } /// Returns the symbol index from a symbol of which its flag is set global, @@ -1679,27 +1702,61 @@ pub fn getDeclVAddr( return target_symbol_index; } -pub fn lowerAnonDecl(self: *Wasm, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { - // This is basically the same as lowerUnnamedConst. - // example: - // const ty = mod.intern_pool.typeOf(decl_val).toType(); - // const val = decl_val.toValue(); - // The symbol name can be something like `__anon_{d}` with `@intFromEnum(decl_val)`. - // It doesn't have an owner decl because it's just an unnamed constant that might - // be used by more than one function, however, its address is being used so we need - // to put it in some location. - // ... - _ = self; - _ = decl_val; - _ = src_loc; - _ = @panic("TODO: link/Wasm lowerAnonDecl"); +pub fn lowerAnonDecl(wasm: *Wasm, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { + const gop = try wasm.anon_decls.getOrPut(wasm.base.allocator, decl_val); + if (gop.found_existing) { + return .ok; + } + + const mod = wasm.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); + const tv: TypedValue = .{ .ty = ty, .val = decl_val.toValue() }; + const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__anon_{d}", .{@intFromEnum(decl_val)}); + defer wasm.base.allocator.free(name); + + switch (try wasm.lowerConst(name, tv, src_loc)) { + .ok => |atom_index| { + gop.value_ptr.* = atom_index; + return .ok; + }, + .fail => |em| return .{ .fail = em }, + } } pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { - _ = wasm; - _ = decl_val; - _ = reloc_info; - _ = @panic("TODO: link/Wasm getAnonDeclVAddr"); + const atom_index = wasm.anon_decls.get(decl_val).?; + const target_symbol_index = wasm.getAtom(atom_index).getSymbolIndex().?; + + const parent_atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; + const parent_atom = wasm.getAtomPtr(parent_atom_index); + const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; + const mod = wasm.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); + if (ty.zigTypeTag(mod) == .Fn) { + assert(reloc_info.addend == 0); // addend not allowed for function relocations + // We found a function pointer, so add it to our table, + // as function pointers are not allowed to be stored inside the data section. + // They are instead stored in a function table which are called by index. + try wasm.addTableFunction(target_symbol_index); + try parent_atom.relocs.append(wasm.base.allocator, .{ + .index = target_symbol_index, + .offset = @as(u32, @intCast(reloc_info.offset)), + .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, + }); + } else { + try parent_atom.relocs.append(wasm.base.allocator, .{ + .index = target_symbol_index, + .offset = @as(u32, @intCast(reloc_info.offset)), + .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, + .addend = @as(i32, @intCast(reloc_info.addend)), + }); + } + + // we do not know the final address at this point, + // as atom allocation will determine the address and relocations + // will calculate and rewrite this. Therefore, we simply return the symbol index + // that was targeted. + return target_symbol_index; } pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void { @@ -3465,6 +3522,15 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.parseAtom(local_atom_index, .{ .data = .read_only }); } } + // parse anonymous declarations + for (wasm.anon_decls.keys(), wasm.anon_decls.values()) |decl_val, atom_index| { + const ty = mod.intern_pool.typeOf(decl_val).toType(); + if (ty.zigTypeTag(mod) == .Fn) { + try wasm.parseAtom(atom_index, .function); + } else { + try wasm.parseAtom(atom_index, .{ .data = .read_only }); + } + } // also parse any backend-generated functions for (wasm.synthetic_functions.items) |atom_index| {