authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-19 15:43:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-19 15:43:06+01:00
logc26cbd561c812a8915bc3c3480358b0f2be10de0
tree93bebc0c00d501352668451471e0d2bd41d530b0
parent322ace70f9fb5c3e8833255d1df033208c448349
parent4e0d7154b1a701b906f3d9c5401dc0109253971f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14998 from Luukdegram/shared-mem

wasm-linker: Implement shared-memory

9 files changed, 716 insertions(+), 76 deletions(-)

lib/std/wasm.zig+105-3
...@@ -189,7 +189,9 @@ pub const Opcode = enum(u8) {...@@ -189,7 +189,9 @@ pub const Opcode = enum(u8) {
189 i64_extend16_s = 0xC3,189 i64_extend16_s = 0xC3,
190 i64_extend32_s = 0xC4,190 i64_extend32_s = 0xC4,
191191
192 prefixed = 0xFC,192 misc_prefix = 0xFC,
193 simd_prefix = 0xFD,
194 atomics_prefix = 0xFE,
193 _,195 _,
194};196};
195197
...@@ -217,7 +219,7 @@ test "Wasm - opcodes" {...@@ -217,7 +219,7 @@ test "Wasm - opcodes" {
217/// Opcodes that require a prefix `0xFC`219/// Opcodes that require a prefix `0xFC`
218/// Each opcode represents a varuint32, meaning220/// Each opcode represents a varuint32, meaning
219/// they are encoded as leb128 in binary.221/// they are encoded as leb128 in binary.
220pub const PrefixedOpcode = enum(u32) {222pub const MiscOpcode = enum(u32) {
221 i32_trunc_sat_f32_s = 0x00,223 i32_trunc_sat_f32_s = 0x00,
222 i32_trunc_sat_f32_u = 0x01,224 i32_trunc_sat_f32_u = 0x01,
223 i32_trunc_sat_f64_s = 0x02,225 i32_trunc_sat_f64_s = 0x02,
...@@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {...@@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {
239 _,241 _,
240};242};
241243
244/// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler
245/// to write instructions to the wasm binary file
246pub fn miscOpcode(op: MiscOpcode) u32 {
247 return @enumToInt(op);
248}
249
242/// Simd opcodes that require a prefix `0xFD`.250/// Simd opcodes that require a prefix `0xFD`.
243/// Each opcode represents a varuint32, meaning251/// Each opcode represents a varuint32, meaning
244/// they are encoded as leb128 in binary.252/// they are encoded as leb128 in binary.
...@@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {...@@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {
510 return @enumToInt(op);518 return @enumToInt(op);
511}519}
512520
521/// Simd opcodes that require a prefix `0xFE`.
522/// Each opcode represents a varuint32, meaning
523/// they are encoded as leb128 in binary.
524pub const AtomicsOpcode = enum(u32) {
525 memory_atomic_notify = 0x00,
526 memory_atomic_wait32 = 0x01,
527 memory_atomic_wait64 = 0x02,
528 atomic_fence = 0x03,
529 i32_atomic_load = 0x10,
530 i64_atomic_load = 0x11,
531 i32_atomic_load8_u = 0x12,
532 i32_atomic_load16_u = 0x13,
533 i64_atomic_load8_u = 0x14,
534 i64_atomic_load16_u = 0x15,
535 i64_atomic_load32_u = 0x16,
536 i32_atomic_store = 0x17,
537 i64_atomic_store = 0x18,
538 i32_atomic_store8 = 0x19,
539 i32_atomic_store16 = 0x1A,
540 i64_atomic_store8 = 0x1B,
541 i64_atomic_store16 = 0x1C,
542 i64_atomic_store32 = 0x1D,
543 i32_atomic_rmw_add = 0x1E,
544 i64_atomic_rmw_add = 0x1F,
545 i32_atomic_rmw8_add_u = 0x20,
546 i32_atomic_rmw16_add_u = 0x21,
547 i64_atomic_rmw8_add_u = 0x22,
548 i64_atomic_rmw16_add_u = 0x23,
549 i64_atomic_rmw32_add_u = 0x24,
550 i32_atomic_rmw_sub = 0x25,
551 i64_atomic_rmw_sub = 0x26,
552 i32_atomic_rmw8_sub_u = 0x27A,
553 i32_atomic_rmw16_sub_u = 0x28A,
554 i64_atomic_rmw8_sub_u = 0x29A,
555 i64_atomic_rmw16_sub_u = 0x2A,
556 i64_atomic_rmw32_sub_u = 0x2B,
557 i32_atomic_rmw_and = 0x2C,
558 i64_atomic_rmw_and = 0x2D,
559 i32_atomic_rmw8_and_u = 0x2E,
560 i32_atomic_rmw16_and_u = 0x2F,
561 i64_atomic_rmw8_and_u = 0x30,
562 i64_atomic_rmw16_and_u = 0x31,
563 i64_atomic_rmw32_and_u = 0x32,
564 i32_atomic_rmw_or = 0x33,
565 i64_atomic_rmw_or = 0x34,
566 i32_atomic_rmw8_or_u = 0x35,
567 i32_atomic_rmw16_or_u = 0x36,
568 i64_atomic_rmw8_or_u = 0x37,
569 i64_atomic_rmw16_or_u = 0x38,
570 i64_atomic_rmw32_or_u = 0x39,
571 i32_atomic_rmw_xor = 0x3A,
572 i64_atomic_rmw_xor = 0x3B,
573 i32_atomic_rmw8_xor_u = 0x3C,
574 i32_atomic_rmw16_xor_u = 0x3D,
575 i64_atomic_rmw8_xor_u = 0x3E,
576 i64_atomic_rmw16_xor_u = 0x3F,
577 i64_atomic_rmw32_xor_u = 0x40,
578 i32_atomic_rmw_xchg = 0x41,
579 i64_atomic_rmw_xchg = 0x42,
580 i32_atomic_rmw8_xchg_u = 0x43,
581 i32_atomic_rmw16_xchg_u = 0x44,
582 i64_atomic_rmw8_xchg_u = 0x45,
583 i64_atomic_rmw16_xchg_u = 0x46,
584 i64_atomic_rmw32_xchg_u = 0x47,
585
586 i32_atomic_rmw_cmpxchg = 0x48,
587 i64_atomic_rmw_cmpxchg = 0x49,
588 i32_atomic_rmw8_cmpxchg_u = 0x4A,
589 i32_atomic_rmw16_cmpxchg_u = 0x4B,
590 i64_atomic_rmw8_cmpxchg_u = 0x4C,
591 i64_atomic_rmw16_cmpxchg_u = 0x4D,
592 i64_atomic_rmw32_cmpxchg_u = 0x4E,
593};
594
595/// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler
596/// to write instructions to the wasm binary file
597pub fn atomicsOpcode(op: AtomicsOpcode) u32 {
598 return @enumToInt(op);
599}
600
513/// Enum representing all Wasm value types as per spec:601/// Enum representing all Wasm value types as per spec:
514/// https://webassembly.github.io/spec/core/binary/types.html602/// https://webassembly.github.io/spec/core/binary/types.html
515pub const Valtype = enum(u8) {603pub const Valtype = enum(u8) {
...@@ -551,8 +639,22 @@ test "Wasm - valtypes" {...@@ -551,8 +639,22 @@ test "Wasm - valtypes" {
551639
552/// Limits classify the size range of resizeable storage associated with memory types and table types.640/// Limits classify the size range of resizeable storage associated with memory types and table types.
553pub const Limits = struct {641pub const Limits = struct {
642 flags: u8,
554 min: u32,643 min: u32,
555 max: ?u32,644 max: u32,
645
646 pub const Flags = enum(u8) {
647 WASM_LIMITS_FLAG_HAS_MAX = 0x1,
648 WASM_LIMITS_FLAG_IS_SHARED = 0x2,
649 };
650
651 pub fn hasFlag(limits: Limits, flag: Flags) bool {
652 return limits.flags & @enumToInt(flag) != 0;
653 }
654
655 pub fn setFlag(limits: *Limits, flag: Flags) void {
656 limits.flags |= @enumToInt(flag);
657 }
556};658};
557659
558/// Initialization expressions are used to set the initial value on an object660/// Initialization expressions are used to set the initial value on an object
src/arch/wasm/CodeGen.zig+7-7
...@@ -895,10 +895,10 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {...@@ -895,10 +895,10 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
895 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });895 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
896}896}
897897
898fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {898fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {
899 const extra_index = @intCast(u32, func.mir_extra.items.len);899 const extra_index = @intCast(u32, func.mir_extra.items.len);
900 try func.mir_extra.append(func.gpa, @enumToInt(opcode));900 try func.mir_extra.append(func.gpa, @enumToInt(opcode));
901 try func.addInst(.{ .tag = .extended, .data = .{ .payload = extra_index } });901 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
902}902}
903903
904fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {904fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
...@@ -925,7 +925,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {...@@ -925,7 +925,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
925 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);925 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
926 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));926 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
927 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));927 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
928 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });928 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
929}929}
930930
931fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {931fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
...@@ -2310,7 +2310,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2310,7 +2310,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2310 offset + lhs.offset(),2310 offset + lhs.offset(),
2311 ty.abiAlignment(func.target),2311 ty.abiAlignment(func.target),
2312 });2312 });
2313 return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });2313 return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2314 },2314 },
2315 },2315 },
2316 .Pointer => {2316 .Pointer => {
...@@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2420 offset + operand.offset(),2420 offset + operand.offset(),
2421 ty.abiAlignment(func.target),2421 ty.abiAlignment(func.target),
2422 });2422 });
2423 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });2423 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2424 return WValue{ .stack = {} };2424 return WValue{ .stack = {} };
2425 }2425 }
24262426
...@@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4477 operand.offset(),4477 operand.offset(),
4478 elem_ty.abiAlignment(func.target),4478 elem_ty.abiAlignment(func.target),
4479 });4479 });
4480 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });4480 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4481 try func.addLabel(.local_set, result.local.value);4481 try func.addLabel(.local_set, result.local.value);
4482 return func.finishAir(inst, result, &.{ty_op.operand});4482 return func.finishAir(inst, result, &.{ty_op.operand});
4483 },4483 },
...@@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4493 try func.emitWValue(operand);4493 try func.emitWValue(operand);
4494 const extra_index = @intCast(u32, func.mir_extra.items.len);4494 const extra_index = @intCast(u32, func.mir_extra.items.len);
4495 try func.mir_extra.append(func.gpa, opcode);4495 try func.mir_extra.append(func.gpa, opcode);
4496 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });4496 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4497 try func.addLabel(.local_set, result.local.value);4497 try func.addLabel(.local_set, result.local.value);
4498 return func.finishAir(inst, result, &.{ty_op.operand});4498 return func.finishAir(inst, result, &.{ty_op.operand});
4499 },4499 },
src/arch/wasm/Emit.zig+12-6
...@@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {
239 .i64_clz => try emit.emitTag(tag),239 .i64_clz => try emit.emitTag(tag),
240 .i64_ctz => try emit.emitTag(tag),240 .i64_ctz => try emit.emitTag(tag),
241241
242 .extended => try emit.emitExtended(inst),242 .misc_prefix => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),243 .simd_prefix => try emit.emitSimd(inst),
244 .atomics_prefix => try emit.emitAtomic(inst),
244 }245 }
245 }246 }
246}247}
...@@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
433 const extra_index = emit.mir.instructions.items(.data)[inst].payload;434 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
434 const opcode = emit.mir.extra[extra_index];435 const opcode = emit.mir.extra[extra_index];
435 const writer = emit.code.writer();436 const writer = emit.code.writer();
436 try emit.code.append(0xFC);437 try emit.code.append(std.wasm.opcode(.misc_prefix));
437 try leb128.writeULEB128(writer, opcode);438 try leb128.writeULEB128(writer, opcode);
438 switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {439 switch (@intToEnum(std.wasm.MiscOpcode, opcode)) {
439 // bulk-memory opcodes440 // bulk-memory opcodes
440 .data_drop => {441 .data_drop => {
441 const segment = emit.mir.extra[extra_index + 1];442 const segment = emit.mir.extra[extra_index + 1];
...@@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
472 const extra_index = emit.mir.instructions.items(.data)[inst].payload;473 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
473 const opcode = emit.mir.extra[extra_index];474 const opcode = emit.mir.extra[extra_index];
474 const writer = emit.code.writer();475 const writer = emit.code.writer();
475 try emit.code.append(0xFD);476 try emit.code.append(std.wasm.opcode(.simd_prefix));
476 try leb128.writeULEB128(writer, opcode);477 try leb128.writeULEB128(writer, opcode);
477 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {478 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
478 .v128_store,479 .v128_store,
...@@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
496 .f32x4_splat,497 .f32x4_splat,
497 .f64x2_splat,498 .f64x2_splat,
498 => {}, // opcode already written499 => {}, // opcode already written
499 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}\n", .{@tagName(tag)}),500 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
500 }501 }
501}502}
502503
504fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
505 _ = inst;
506 return emit.fail("TODO: Implement atomics instructions", .{});
507}
508
503fn emitMemFill(emit: *Emit) !void {509fn emitMemFill(emit: *Emit) !void {
504 try emit.code.append(0xFC);510 try emit.code.append(0xFC);
505 try emit.code.append(0x0B);511 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+15-8
...@@ -87,6 +87,13 @@ pub const Inst = struct {...@@ -87,6 +87,13 @@ pub const Inst = struct {
87 ///87 ///
88 /// Uses `label`88 /// Uses `label`
89 call_indirect = 0x11,89 call_indirect = 0x11,
90 /// Contains a symbol to a function pointer
91 /// uses `label`
92 ///
93 /// Note: This uses `0x16` as value which is reserved by the WebAssembly
94 /// specification but unused, meaning we must update this if the specification were to
95 /// use this value.
96 function_index = 0x16,
90 /// Pops three values from the stack and pushes97 /// Pops three values from the stack and pushes
91 /// the first or second value dependent on the third value.98 /// the first or second value dependent on the third value.
92 /// Uses `tag`99 /// Uses `tag`
...@@ -510,24 +517,24 @@ pub const Inst = struct {...@@ -510,24 +517,24 @@ pub const Inst = struct {
510 i64_extend16_s = 0xC3,517 i64_extend16_s = 0xC3,
511 /// Uses `tag`518 /// Uses `tag`
512 i64_extend32_s = 0xC4,519 i64_extend32_s = 0xC4,
513 /// The instruction consists of an extension opcode.520 /// The instruction consists of a prefixed opcode.
514 /// The prefixed opcode can be found at payload's index.521 /// The prefixed opcode can be found at payload's index.
515 ///522 ///
516 /// The `data` field depends on the extension instruction and523 /// The `data` field depends on the extension instruction and
517 /// may contain additional data.524 /// may contain additional data.
518 extended = 0xFC,525 misc_prefix = 0xFC,
519 /// The instruction consists of a simd opcode.526 /// The instruction consists of a simd opcode.
520 /// The actual simd-opcode is found at payload's index.527 /// The actual simd-opcode is found at payload's index.
521 ///528 ///
522 /// The `data` field depends on the simd instruction and529 /// The `data` field depends on the simd instruction and
523 /// may contain additional data.530 /// may contain additional data.
524 simd = 0xFD,531 simd_prefix = 0xFD,
525 /// Contains a symbol to a function pointer532 /// The instruction consists of an atomics opcode.
526 /// uses `label`533 /// The actual atomics-opcode is found at payload's index.
527 ///534 ///
528 /// Note: This uses `0xFE` as value as it is unused and not reserved535 /// The `data` field depends on the atomics instruction and
529 /// by the wasm specification, making it safe to use.536 /// may contain additional data.
530 function_index = 0xFE,537 atomics_prefix = 0xFE,
531 /// Contains a symbol to a memory address538 /// Contains a symbol to a memory address
532 /// Uses `label`539 /// Uses `label`
533 ///540 ///
src/link/Wasm.zig+498-18
...@@ -111,7 +111,11 @@ functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std....@@ -111,7 +111,11 @@ functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.
111/// Output global section111/// Output global section
112wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},112wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
113/// Memory section113/// Memory section
114memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },114memories: std.wasm.Memory = .{ .limits = .{
115 .min = 0,
116 .max = undefined,
117 .flags = 0,
118} },
115/// Output table section119/// Output table section
116tables: std.ArrayListUnmanaged(std.wasm.Table) = .{},120tables: std.ArrayListUnmanaged(std.wasm.Table) = .{},
117/// Output export section121/// Output export section
...@@ -135,6 +139,8 @@ archives: std.ArrayListUnmanaged(Archive) = .{},...@@ -135,6 +139,8 @@ archives: std.ArrayListUnmanaged(Archive) = .{},
135139
136/// A map of global names (read: offset into string table) to their symbol location140/// A map of global names (read: offset into string table) to their symbol location
137globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{},141globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{},
142/// The list of GOT symbols and their location
143got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
138/// Maps discarded symbols and their positions to the location of the symbol144/// Maps discarded symbols and their positions to the location of the symbol
139/// it was resolved to145/// it was resolved to
140discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},146discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
...@@ -176,6 +182,24 @@ pub const Segment = struct {...@@ -176,6 +182,24 @@ pub const Segment = struct {
176 alignment: u32,182 alignment: u32,
177 size: u32,183 size: u32,
178 offset: u32,184 offset: u32,
185 flags: u32,
186
187 pub const Flag = enum(u32) {
188 WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,
189 WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,
190 };
191
192 pub fn isPassive(segment: Segment) bool {
193 return segment.flags & @enumToInt(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;
194 }
195
196 /// For a given segment, determines if it needs passive initialization
197 fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool {
198 if (import_mem and !std.mem.eql(u8, name, ".bss")) {
199 return true;
200 }
201 return segment.isPassive();
202 }
179};203};
180204
181pub const Export = struct {205pub const Export = struct {
...@@ -396,7 +420,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -396,7 +420,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
396 const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table);420 const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table);
397 const symbol = loc.getSymbol(wasm_bin);421 const symbol = loc.getSymbol(wasm_bin);
398 const table: std.wasm.Table = .{422 const table: std.wasm.Table = .{
399 .limits = .{ .min = 0, .max = null }, // will be overwritten during `mapFunctionTable`423 .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable`
400 .reftype = .funcref,424 .reftype = .funcref,
401 };425 };
402 if (options.output_mode == .Obj or options.import_table) {426 if (options.output_mode == .Obj or options.import_table) {
...@@ -429,6 +453,30 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -429,6 +453,30 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
429 // at the end during `initializeCallCtorsFunction`.453 // at the end during `initializeCallCtorsFunction`.
430 }454 }
431455
456 // shared-memory symbols for TLS support
457 if (wasm_bin.base.options.shared_memory) {
458 {
459 const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);
460 const symbol = loc.getSymbol(wasm_bin);
461 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
462 }
463 {
464 const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);
465 const symbol = loc.getSymbol(wasm_bin);
466 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
467 }
468 {
469 const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);
470 const symbol = loc.getSymbol(wasm_bin);
471 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
472 }
473 {
474 const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function);
475 const symbol = loc.getSymbol(wasm_bin);
476 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
477 }
478 }
479
432 // if (!options.strip and options.module != null) {480 // if (!options.strip and options.module != null) {
433 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);481 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
434 // try wasm_bin.initDebugSections();482 // try wasm_bin.initDebugSections();
...@@ -597,6 +645,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -597,6 +645,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
597 return true;645 return true;
598}646}
599647
648fn requiresTLSReloc(wasm: *const Wasm) bool {
649 for (wasm.got_symbols.items) |loc| {
650 if (loc.getSymbol(wasm).isTLS()) {
651 return true;
652 }
653 }
654 return false;
655}
656
600fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {657fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
601 const object: Object = wasm.objects.items[object_index];658 const object: Object = wasm.objects.items[object_index];
602 log.debug("Resolving symbols in object: '{s}'", .{object.name});659 log.debug("Resolving symbols in object: '{s}'", .{object.name});
...@@ -775,6 +832,220 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -775,6 +832,220 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
775 }832 }
776}833}
777834
835fn setupInitMemoryFunction(wasm: *Wasm) !void {
836 // Passive segments are used to avoid memory being reinitialized on each
837 // thread's instantiation. These passive segments are initialized and
838 // dropped in __wasm_init_memory, which is registered as the start function
839 // We also initialize bss segments (using memory.fill) as part of this
840 // function.
841 if (!wasm.hasPassiveInitializationSegments()) {
842 return;
843 }
844
845 const flag_address: u32 = if (wasm.base.options.shared_memory) address: {
846 // when we have passive initialization segments and shared memory
847 // `setupMemory` will create this symbol and set its virtual address.
848 const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?;
849 break :address loc.getSymbol(wasm).virtual_address;
850 } else 0;
851
852 var function_body = std.ArrayList(u8).init(wasm.base.allocator);
853 defer function_body.deinit();
854 const writer = function_body.writer();
855
856 // we have 0 locals
857 try leb.writeULEB128(writer, @as(u32, 0));
858
859 if (wasm.base.options.shared_memory) {
860 // destination blocks
861 // based on values we jump to corresponding label
862 try writer.writeByte(std.wasm.opcode(.block)); // $drop
863 try writer.writeByte(std.wasm.block_empty); // block type
864
865 try writer.writeByte(std.wasm.opcode(.block)); // $wait
866 try writer.writeByte(std.wasm.block_empty); // block type
867
868 try writer.writeByte(std.wasm.opcode(.block)); // $init
869 try writer.writeByte(std.wasm.block_empty); // block type
870
871 // atomically check
872 try writer.writeByte(std.wasm.opcode(.i32_const));
873 try leb.writeULEB128(writer, flag_address);
874 try writer.writeByte(std.wasm.opcode(.i32_const));
875 try leb.writeULEB128(writer, @as(u32, 0));
876 try writer.writeByte(std.wasm.opcode(.i32_const));
877 try leb.writeULEB128(writer, @as(u32, 1));
878 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
879 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
880 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
881 try leb.writeULEB128(writer, @as(u32, 0)); // offset
882
883 // based on the value from the atomic check, jump to the label.
884 try writer.writeByte(std.wasm.opcode(.br_table));
885 try leb.writeULEB128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
886 try leb.writeULEB128(writer, @as(u32, 0)); // $init
887 try leb.writeULEB128(writer, @as(u32, 1)); // $wait
888 try leb.writeULEB128(writer, @as(u32, 2)); // $drop
889 try writer.writeByte(std.wasm.opcode(.end));
890 }
891
892 var it = wasm.data_segments.iterator();
893 var segment_index: u32 = 0;
894 while (it.next()) |entry| : (segment_index += 1) {
895 const segment: Segment = wasm.segments.items[entry.value_ptr.*];
896 if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) {
897 // For passive BSS segments we can simple issue a memory.fill(0).
898 // For non-BSS segments we do a memory.init. Both these
899 // instructions take as their first argument the destination
900 // address.
901 try writer.writeByte(std.wasm.opcode(.i32_const));
902 try leb.writeULEB128(writer, segment.offset);
903
904 if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
905 // When we initialize the TLS segment we also set the `__tls_base`
906 // global. This allows the runtime to use this static copy of the
907 // TLS data for the first/main thread.
908 try writer.writeByte(std.wasm.opcode(.i32_const));
909 try leb.writeULEB128(writer, segment.offset);
910 try writer.writeByte(std.wasm.opcode(.global_set));
911 const loc = wasm.findGlobalSymbol("__tls_base").?;
912 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
913 }
914
915 try writer.writeByte(std.wasm.opcode(.i32_const));
916 try leb.writeULEB128(writer, @as(u32, 0));
917 try writer.writeByte(std.wasm.opcode(.i32_const));
918 try leb.writeULEB128(writer, segment.size);
919 try writer.writeByte(std.wasm.opcode(.misc_prefix));
920 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {
921 // fill bss segment with zeroes
922 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_fill));
923 } else {
924 // initialize the segment
925 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
926 try leb.writeULEB128(writer, segment_index);
927 }
928 try writer.writeByte(0); // memory index immediate
929 }
930 }
931
932 if (wasm.base.options.shared_memory) {
933 // we set the init memory flag to value '2'
934 try writer.writeByte(std.wasm.opcode(.i32_const));
935 try leb.writeULEB128(writer, flag_address);
936 try writer.writeByte(std.wasm.opcode(.i32_const));
937 try leb.writeULEB128(writer, @as(u32, 2));
938 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
939 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
940 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
941 try leb.writeULEB128(writer, @as(u32, 0)); // offset
942
943 // notify any waiters for segment initialization completion
944 try writer.writeByte(std.wasm.opcode(.i32_const));
945 try leb.writeULEB128(writer, flag_address);
946 try writer.writeByte(std.wasm.opcode(.i32_const));
947 try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters
948 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
949 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_notify));
950 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
951 try leb.writeULEB128(writer, @as(u32, 0)); // offset
952 try writer.writeByte(std.wasm.opcode(.drop));
953
954 // branch and drop segments
955 try writer.writeByte(std.wasm.opcode(.br));
956 try leb.writeULEB128(writer, @as(u32, 1));
957
958 // wait for thread to initialize memory segments
959 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
960 try writer.writeByte(std.wasm.opcode(.i32_const));
961 try leb.writeULEB128(writer, flag_address);
962 try writer.writeByte(std.wasm.opcode(.i32_const));
963 try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value
964 try writer.writeByte(std.wasm.opcode(.i32_const));
965 try leb.writeILEB128(writer, @as(i32, -1)); // timeout
966 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
967 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
968 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
969 try leb.writeULEB128(writer, @as(u32, 0)); // offset
970 try writer.writeByte(std.wasm.opcode(.drop));
971
972 try writer.writeByte(std.wasm.opcode(.end)); // end $drop
973 }
974
975 it.reset();
976 segment_index = 0;
977 while (it.next()) |entry| : (segment_index += 1) {
978 const name = entry.key_ptr.*;
979 const segment: Segment = wasm.segments.items[entry.value_ptr.*];
980 if (segment.needsPassiveInitialization(wasm.base.options.import_memory, name) and
981 !std.mem.eql(u8, name, ".bss"))
982 {
983 // The TLS region should not be dropped since its is needed
984 // during the initialization of each thread (__wasm_init_tls).
985 if (wasm.base.options.shared_memory and std.mem.eql(u8, name, ".tdata")) {
986 continue;
987 }
988
989 try writer.writeByte(std.wasm.opcode(.misc_prefix));
990 try leb.writeULEB128(writer, std.wasm.miscOpcode(.data_drop));
991 try leb.writeULEB128(writer, segment_index);
992 }
993 }
994
995 // End of the function body
996 try writer.writeByte(std.wasm.opcode(.end));
997
998 try wasm.createSyntheticFunction(
999 "__wasm_init_memory",
1000 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1001 &function_body,
1002 );
1003}
1004
1005/// Constructs a synthetic function that performs runtime relocations for
1006/// TLS symbols. This function is called by `__wasm_init_tls`.
1007fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
1008 // When we have TLS GOT entries and shared memory is enabled,
1009 // we must perform runtime relocations or else we don't create the function.
1010 if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) {
1011 return;
1012 }
1013
1014 // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs");
1015 var function_body = std.ArrayList(u8).init(wasm.base.allocator);
1016 defer function_body.deinit();
1017 const writer = function_body.writer();
1018
1019 // locals (we have none)
1020 try writer.writeByte(0);
1021 for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
1022 const sym: *Symbol = got_loc.getSymbol(wasm);
1023 if (!sym.isTLS()) continue; // only relocate TLS symbols
1024 if (sym.tag == .data and sym.isDefined()) {
1025 // get __tls_base
1026 try writer.writeByte(std.wasm.opcode(.global_get));
1027 try leb.writeULEB128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index);
1028
1029 // add the virtual address of the symbol
1030 try writer.writeByte(std.wasm.opcode(.i32_const));
1031 try leb.writeULEB128(writer, sym.virtual_address);
1032 } else if (sym.tag == .function) {
1033 @panic("TODO: relocate GOT entry of function");
1034 } else continue;
1035
1036 try writer.writeByte(std.wasm.opcode(.i32_add));
1037 try writer.writeByte(std.wasm.opcode(.global_set));
1038 try leb.writeULEB128(writer, wasm.imported_globals_count + @intCast(u32, wasm.wasm_globals.items.len + got_index));
1039 }
1040 try writer.writeByte(std.wasm.opcode(.end));
1041
1042 try wasm.createSyntheticFunction(
1043 "__wasm_apply_global_tls_relocs",
1044 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1045 &function_body,
1046 );
1047}
1048
778fn validateFeatures(1049fn validateFeatures(
779 wasm: *const Wasm,1050 wasm: *const Wasm,
780 to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,1051 to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
...@@ -791,6 +1062,8 @@ fn validateFeatures(...@@ -791,6 +1062,8 @@ fn validateFeatures(
7911062
792 // when false, we fail linking. We only verify this after a loop to catch all invalid features.1063 // when false, we fail linking. We only verify this after a loop to catch all invalid features.
793 var valid_feature_set = true;1064 var valid_feature_set = true;
1065 // will be set to true when there's any TLS segment found in any of the object files
1066 var has_tls = false;
7941067
795 // When the user has given an explicit list of features to enable,1068 // When the user has given an explicit list of features to enable,
796 // we extract them and insert each into the 'allowed' list.1069 // we extract them and insert each into the 'allowed' list.
...@@ -821,6 +1094,12 @@ fn validateFeatures(...@@ -821,6 +1094,12 @@ fn validateFeatures(
821 },1094 },
822 }1095 }
823 }1096 }
1097
1098 for (object.segment_info) |segment| {
1099 if (segment.isTLS()) {
1100 has_tls = true;
1101 }
1102 }
824 }1103 }
8251104
826 // when we infer the features, we allow each feature found in the 'used' set1105 // when we infer the features, we allow each feature found in the 'used' set
...@@ -832,7 +1111,7 @@ fn validateFeatures(...@@ -832,7 +1111,7 @@ fn validateFeatures(
832 allowed[used_index] = is_enabled;1111 allowed[used_index] = is_enabled;
833 emit_features_count.* += @boolToInt(is_enabled);1112 emit_features_count.* += @boolToInt(is_enabled);
834 } else if (is_enabled and !allowed[used_index]) {1113 } else if (is_enabled and !allowed[used_index]) {
835 log.err("feature '{s}' not allowed, but used by linked object", .{(@intToEnum(types.Feature.Tag, used_index)).toString()});1114 log.err("feature '{}' not allowed, but used by linked object", .{@intToEnum(types.Feature.Tag, used_index)});
836 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});1115 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});
837 valid_feature_set = false;1116 valid_feature_set = false;
838 }1117 }
...@@ -842,6 +1121,30 @@ fn validateFeatures(...@@ -842,6 +1121,30 @@ fn validateFeatures(
842 return error.InvalidFeatureSet;1121 return error.InvalidFeatureSet;
843 }1122 }
8441123
1124 if (wasm.base.options.shared_memory) {
1125 const disallowed_feature = disallowed[@enumToInt(types.Feature.Tag.shared_mem)];
1126 if (@truncate(u1, disallowed_feature) != 0) {
1127 log.err(
1128 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1129 .{wasm.objects.items[disallowed_feature >> 1].name},
1130 );
1131 valid_feature_set = false;
1132 }
1133
1134 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1135 if (!allowed[@enumToInt(feature)]) {
1136 log.err("feature '{}' is not used but is required for shared-memory", .{feature});
1137 }
1138 }
1139 }
1140
1141 if (has_tls) {
1142 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1143 if (!allowed[@enumToInt(feature)]) {
1144 log.err("feature '{}' is not used but is required for thread-local storage", .{feature});
1145 }
1146 }
1147 }
845 // For each linked object, validate the required and disallowed features1148 // For each linked object, validate the required and disallowed features
846 for (wasm.objects.items) |object| {1149 for (wasm.objects.items) |object| {
847 var object_used_features = [_]bool{false} ** known_features_count;1150 var object_used_features = [_]bool{false} ** known_features_count;
...@@ -850,7 +1153,7 @@ fn validateFeatures(...@@ -850,7 +1153,7 @@ fn validateFeatures(
850 // from here a feature is always used1153 // from here a feature is always used
851 const disallowed_feature = disallowed[@enumToInt(feature.tag)];1154 const disallowed_feature = disallowed[@enumToInt(feature.tag)];
852 if (@truncate(u1, disallowed_feature) != 0) {1155 if (@truncate(u1, disallowed_feature) != 0) {
853 log.err("feature '{s}' is disallowed, but used by linked object", .{feature.tag.toString()});1156 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});
854 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});1157 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});
855 log.err(" used in '{s}'", .{object.name});1158 log.err(" used in '{s}'", .{object.name});
856 valid_feature_set = false;1159 valid_feature_set = false;
...@@ -863,7 +1166,7 @@ fn validateFeatures(...@@ -863,7 +1166,7 @@ fn validateFeatures(
863 for (required, 0..) |required_feature, feature_index| {1166 for (required, 0..) |required_feature, feature_index| {
864 const is_required = @truncate(u1, required_feature) != 0;1167 const is_required = @truncate(u1, required_feature) != 0;
865 if (is_required and !object_used_features[feature_index]) {1168 if (is_required and !object_used_features[feature_index]) {
866 log.err("feature '{s}' is required but not used in linked object", .{(@intToEnum(types.Feature.Tag, feature_index)).toString()});1169 log.err("feature '{}' is required but not used in linked object", .{@intToEnum(types.Feature.Tag, feature_index)});
867 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});1170 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});
868 log.err(" missing in '{s}'", .{object.name});1171 log.err(" missing in '{s}'", .{object.name});
869 valid_feature_set = false;1172 valid_feature_set = false;
...@@ -894,6 +1197,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -894,6 +1197,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);1197 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
895 _ = wasm.resolved_symbols.swapRemove(loc);1198 _ = wasm.resolved_symbols.swapRemove(loc);
896 }1199 }
1200
1201 if (!wasm.base.options.shared_memory) {
1202 if (wasm.undefs.fetchSwapRemove("__tls_base")) |kv| {
1203 const loc = try wasm.createSyntheticSymbol("__tls_base", .global);
1204 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
1205 }
1206 }
897}1207}
8981208
899// Tries to find a global symbol by its name. Returns null when not found,1209// Tries to find a global symbol by its name. Returns null when not found,
...@@ -1517,7 +1827,7 @@ fn mapFunctionTable(wasm: *Wasm) void {...@@ -1517,7 +1827,7 @@ fn mapFunctionTable(wasm: *Wasm) void {
1517 const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?;1827 const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
1518 const symbol = sym_loc.getSymbol(wasm);1828 const symbol = sym_loc.getSymbol(wasm);
1519 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];1829 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
1520 table.limits = .{ .min = index, .max = index };1830 table.limits = .{ .min = index, .max = index, .flags = 0x1 };
1521 }1831 }
1522}1832}
15231833
...@@ -1630,6 +1940,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -1630,6 +1940,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1630 .alignment = atom.alignment,1940 .alignment = atom.alignment,
1631 .size = atom.size,1941 .size = atom.size,
1632 .offset = 0,1942 .offset = 0,
1943 .flags = 0,
1633 });1944 });
1634 }1945 }
16351946
...@@ -1668,10 +1979,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -1668,10 +1979,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1668 break :result index;1979 break :result index;
1669 } else {1980 } else {
1670 const index = @intCast(u32, wasm.segments.items.len);1981 const index = @intCast(u32, wasm.segments.items.len);
1982 var flags: u32 = 0;
1983 if (wasm.base.options.shared_memory) {
1984 flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
1985 }
1671 try wasm.segments.append(wasm.base.allocator, .{1986 try wasm.segments.append(wasm.base.allocator, .{
1672 .alignment = atom.alignment,1987 .alignment = atom.alignment,
1673 .size = 0,1988 .size = 0,
1674 .offset = 0,1989 .offset = 0,
1990 .flags = flags,
1675 });1991 });
1676 gop.value_ptr.* = index;1992 gop.value_ptr.* = index;
16771993
...@@ -1907,10 +2223,23 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1907,10 +2223,23 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1907 try writer.writeByte(std.wasm.opcode(.end));2223 try writer.writeByte(std.wasm.opcode(.end));
1908 }2224 }
19092225
1910 const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?;2226 try wasm.createSyntheticFunction(
2227 "__wasm_call_ctors",
2228 std.wasm.Type{ .params = &.{}, .returns = &.{} },
2229 &function_body,
2230 );
2231}
2232
2233fn createSyntheticFunction(
2234 wasm: *Wasm,
2235 symbol_name: []const u8,
2236 func_ty: std.wasm.Type,
2237 function_body: *std.ArrayList(u8),
2238) !void {
2239 const loc = wasm.findGlobalSymbol(symbol_name) orelse
2240 try wasm.createSyntheticSymbol(symbol_name, .function);
1911 const symbol = loc.getSymbol(wasm);2241 const symbol = loc.getSymbol(wasm);
1912 // create type (() -> nil) as we do not have any parameters or return value.2242 const ty_index = try wasm.putOrGetFuncType(func_ty);
1913 const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} });
1914 // create function with above type2243 // create function with above type
1915 const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count());2244 const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count());
1916 try wasm.functions.putNoClobber(2245 try wasm.functions.putNoClobber(
...@@ -1942,6 +2271,68 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1942,6 +2271,68 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1942 atom.offset = prev_atom.offset + prev_atom.size;2271 atom.offset = prev_atom.offset + prev_atom.size;
1943}2272}
19442273
2274fn initializeTLSFunction(wasm: *Wasm) !void {
2275 if (!wasm.base.options.shared_memory) return;
2276
2277 var function_body = std.ArrayList(u8).init(wasm.base.allocator);
2278 defer function_body.deinit();
2279 const writer = function_body.writer();
2280
2281 // locals
2282 try writer.writeByte(0);
2283
2284 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
2285 if (wasm.data_segments.getIndex(".tdata")) |data_index| {
2286 const segment_index = wasm.data_segments.entries.items(.value)[data_index];
2287 const segment = wasm.segments.items[segment_index];
2288
2289 const param_local: u32 = 0;
2290
2291 try writer.writeByte(std.wasm.opcode(.local_get));
2292 try leb.writeULEB128(writer, param_local);
2293
2294 const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;
2295 try writer.writeByte(std.wasm.opcode(.global_get));
2296 try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);
2297
2298 // load stack values for the bulk-memory operation
2299 {
2300 try writer.writeByte(std.wasm.opcode(.local_get));
2301 try leb.writeULEB128(writer, param_local);
2302
2303 try writer.writeByte(std.wasm.opcode(.i32_const));
2304 try leb.writeULEB128(writer, @as(u32, 0)); //segment offset
2305
2306 try writer.writeByte(std.wasm.opcode(.i32_const));
2307 try leb.writeULEB128(writer, @as(u32, segment.size)); //segment offset
2308 }
2309
2310 // perform the bulk-memory operation to initialize the data segment
2311 try writer.writeByte(std.wasm.opcode(.misc_prefix));
2312 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
2313 // segment immediate
2314 try leb.writeULEB128(writer, @intCast(u32, data_index));
2315 // memory index immediate (always 0)
2316 try leb.writeULEB128(writer, @as(u32, 0));
2317 }
2318
2319 // If we have to perform any TLS relocations, call the corresponding function
2320 // which performs all runtime TLS relocations. This is a synthetic function,
2321 // generated by the linker.
2322 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
2323 try writer.writeByte(std.wasm.opcode(.call));
2324 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
2325 }
2326
2327 try writer.writeByte(std.wasm.opcode(.end));
2328
2329 try wasm.createSyntheticFunction(
2330 "__wasm_init_tls",
2331 std.wasm.Type{ .params = &.{.i32}, .returns = &.{} },
2332 &function_body,
2333 );
2334}
2335
1945fn setupImports(wasm: *Wasm) !void {2336fn setupImports(wasm: *Wasm) !void {
1946 log.debug("Merging imports", .{});2337 log.debug("Merging imports", .{});
1947 var discarded_it = wasm.discarded.keyIterator();2338 var discarded_it = wasm.discarded.keyIterator();
...@@ -2224,11 +2615,50 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2224,11 +2615,50 @@ fn setupMemory(wasm: *Wasm) !void {
2224 while (data_seg_it.next()) |entry| {2615 while (data_seg_it.next()) |entry| {
2225 const segment = &wasm.segments.items[entry.value_ptr.*];2616 const segment = &wasm.segments.items[entry.value_ptr.*];
2226 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);2617 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
2618
2619 // set TLS-related symbols
2620 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
2621 if (wasm.findGlobalSymbol("__tls_size")) |loc| {
2622 const sym = loc.getSymbol(wasm);
2623 sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count;
2624 try wasm.wasm_globals.append(wasm.base.allocator, .{
2625 .global_type = .{ .valtype = .i32, .mutable = false },
2626 .init = .{ .i32_const = @intCast(i32, segment.size) },
2627 });
2628 }
2629 if (wasm.findGlobalSymbol("__tls_align")) |loc| {
2630 const sym = loc.getSymbol(wasm);
2631 sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count;
2632 try wasm.wasm_globals.append(wasm.base.allocator, .{
2633 .global_type = .{ .valtype = .i32, .mutable = false },
2634 .init = .{ .i32_const = @intCast(i32, segment.alignment) },
2635 });
2636 }
2637 if (wasm.findGlobalSymbol("__tls_base")) |loc| {
2638 const sym = loc.getSymbol(wasm);
2639 sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count;
2640 try wasm.wasm_globals.append(wasm.base.allocator, .{
2641 .global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory },
2642 .init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @intCast(i32, memory_ptr) },
2643 });
2644 }
2645 }
2646
2227 memory_ptr += segment.size;2647 memory_ptr += segment.size;
2228 segment.offset = offset;2648 segment.offset = offset;
2229 offset += segment.size;2649 offset += segment.size;
2230 }2650 }
22312651
2652 // create the memory init flag which is used by the init memory function
2653 if (wasm.base.options.shared_memory and wasm.hasPassiveInitializationSegments()) {
2654 // align to pointer size
2655 memory_ptr = mem.alignForwardGeneric(u64, memory_ptr, 4);
2656 const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
2657 const sym = loc.getSymbol(wasm);
2658 sym.virtual_address = @intCast(u32, memory_ptr);
2659 memory_ptr += 4;
2660 }
2661
2232 if (!place_stack_first and !is_obj) {2662 if (!place_stack_first and !is_obj) {
2233 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);2663 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);
2234 memory_ptr += stack_size;2664 memory_ptr += stack_size;
...@@ -2286,6 +2716,10 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2286,6 +2716,10 @@ fn setupMemory(wasm: *Wasm) !void {
2286 return error.MemoryTooBig;2716 return error.MemoryTooBig;
2287 }2717 }
2288 wasm.memories.limits.max = @intCast(u32, max_memory / page_size);2718 wasm.memories.limits.max = @intCast(u32, max_memory / page_size);
2719 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
2720 if (wasm.base.options.shared_memory) {
2721 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);
2722 }
2289 log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});2723 log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
2290 }2724 }
2291}2725}
...@@ -2305,7 +2739,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32...@@ -2305,7 +2739,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32
2305 const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment));2739 const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment));
2306 if (!result.found_existing) {2740 if (!result.found_existing) {
2307 result.value_ptr.* = index;2741 result.value_ptr.* = index;
2308 try wasm.appendDummySegment();2742 var flags: u32 = 0;
2743 if (wasm.base.options.shared_memory) {
2744 flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
2745 }
2746 try wasm.segments.append(wasm.base.allocator, .{
2747 .alignment = 1,
2748 .size = 0,
2749 .offset = 0,
2750 .flags = flags,
2751 });
2309 return index;2752 return index;
2310 } else return result.value_ptr.*;2753 } else return result.value_ptr.*;
2311 },2754 },
...@@ -2379,6 +2822,7 @@ fn appendDummySegment(wasm: *Wasm) !void {...@@ -2379,6 +2822,7 @@ fn appendDummySegment(wasm: *Wasm) !void {
2379 .alignment = 1,2822 .alignment = 1,
2380 .size = 0,2823 .size = 0,
2381 .offset = 0,2824 .offset = 0,
2825 .flags = 0,
2382 });2826 });
2383}2827}
23842828
...@@ -2746,6 +3190,9 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -2746,6 +3190,9 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
2746 try wasm.mergeSections();3190 try wasm.mergeSections();
2747 try wasm.mergeTypes();3191 try wasm.mergeTypes();
2748 try wasm.initializeCallCtorsFunction();3192 try wasm.initializeCallCtorsFunction();
3193 try wasm.setupInitMemoryFunction();
3194 try wasm.setupTLSRelocationsFunction();
3195 try wasm.initializeTLSFunction();
2749 try wasm.setupExports();3196 try wasm.setupExports();
2750 try wasm.writeToFile(enabled_features, emit_features_count, arena);3197 try wasm.writeToFile(enabled_features, emit_features_count, arena);
27513198
...@@ -2874,6 +3321,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2874,6 +3321,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2874 try wasm.mergeSections();3321 try wasm.mergeSections();
2875 try wasm.mergeTypes();3322 try wasm.mergeTypes();
2876 try wasm.initializeCallCtorsFunction();3323 try wasm.initializeCallCtorsFunction();
3324 try wasm.setupInitMemoryFunction();
3325 try wasm.setupTLSRelocationsFunction();
3326 try wasm.initializeTLSFunction();
2877 try wasm.setupExports();3327 try wasm.setupExports();
2878 try wasm.writeToFile(enabled_features, emit_features_count, arena);3328 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2879}3329}
...@@ -3096,6 +3546,19 @@ fn writeToFile(...@@ -3096,6 +3546,19 @@ fn writeToFile(
3096 section_count += 1;3546 section_count += 1;
3097 }3547 }
30983548
3549 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
3550 const data_segments_count = wasm.data_segments.count() - @boolToInt(wasm.data_segments.contains(".bss") and import_memory);
3551 if (data_segments_count != 0 and wasm.base.options.shared_memory) {
3552 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3553 try writeVecSectionHeader(
3554 binary_bytes.items,
3555 header_offset,
3556 .data_count,
3557 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3558 @intCast(u32, data_segments_count),
3559 );
3560 }
3561
3099 // Code section3562 // Code section
3100 var code_section_size: u32 = 0;3563 var code_section_size: u32 = 0;
3101 if (wasm.code_section_index) |code_index| {3564 if (wasm.code_section_index) |code_index| {
...@@ -3146,7 +3609,7 @@ fn writeToFile(...@@ -3146,7 +3609,7 @@ fn writeToFile(
3146 }3609 }
31473610
3148 // Data section3611 // Data section
3149 if (wasm.data_segments.count() != 0) {3612 if (data_segments_count != 0) {
3150 const header_offset = try reserveVecSectionHeader(&binary_bytes);3613 const header_offset = try reserveVecSectionHeader(&binary_bytes);
31513614
3152 var it = wasm.data_segments.iterator();3615 var it = wasm.data_segments.iterator();
...@@ -3161,10 +3624,15 @@ fn writeToFile(...@@ -3161,10 +3624,15 @@ fn writeToFile(
3161 segment_count += 1;3624 segment_count += 1;
3162 var atom_index = wasm.atoms.get(segment_index).?;3625 var atom_index = wasm.atoms.get(segment_index).?;
31633626
3164 // flag and index to memory section (currently, there can only be 1 memory section in wasm)3627 try leb.writeULEB128(binary_writer, segment.flags);
3165 try leb.writeULEB128(binary_writer, @as(u32, 0));3628 if (segment.flags & @enumToInt(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) {
3629 try leb.writeULEB128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry
3630 }
3631 // when a segment is passive, it's initialized during runtime.
3632 if (!segment.isPassive()) {
3633 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
3634 }
3166 // offset into data section3635 // offset into data section
3167 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
3168 try leb.writeULEB128(binary_writer, segment.size);3636 try leb.writeULEB128(binary_writer, segment.size);
31693637
3170 // fill in the offset table and the data segments3638 // fill in the offset table and the data segments
...@@ -3413,7 +3881,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con...@@ -3413,7 +3881,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con
3413 if (enabled) {3881 if (enabled) {
3414 const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) };3882 const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) };
3415 try leb.writeULEB128(writer, @enumToInt(feature.prefix));3883 try leb.writeULEB128(writer, @enumToInt(feature.prefix));
3416 const string = feature.tag.toString();3884 var buf: [100]u8 = undefined;
3885 const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag});
3417 try leb.writeULEB128(writer, @intCast(u32, string.len));3886 try leb.writeULEB128(writer, @intCast(u32, string.len));
3418 try writer.writeAll(string);3887 try writer.writeAll(string);
3419 }3888 }
...@@ -3507,10 +3976,10 @@ fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: a...@@ -3507,10 +3976,10 @@ fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: a
3507}3976}
35083977
3509fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {3978fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
3510 try leb.writeULEB128(writer, @boolToInt(limits.max != null));3979 try writer.writeByte(limits.flags);
3511 try leb.writeULEB128(writer, limits.min);3980 try leb.writeULEB128(writer, limits.min);
3512 if (limits.max) |max| {3981 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
3513 try leb.writeULEB128(writer, max);3982 try leb.writeULEB128(writer, limits.max);
3514 }3983 }
3515}3984}
35163985
...@@ -4205,6 +4674,17 @@ fn emitDataRelocations(...@@ -4205,6 +4674,17 @@ fn emitDataRelocations(
4205 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);4674 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
4206}4675}
42074676
4677fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
4678 var it = wasm.data_segments.iterator();
4679 while (it.next()) |entry| {
4680 const segment: Segment = wasm.segments.items[entry.value_ptr.*];
4681 if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) {
4682 return true;
4683 }
4684 }
4685 return false;
4686}
4687
4208pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {4688pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
4209 var index: u32 = 0;4689 var index: u32 = 0;
4210 while (index < wasm.func_types.items.len) : (index += 1) {4690 while (index < wasm.func_types.items.len) : (index += 1) {
src/link/Wasm/Atom.zig+7
...@@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {...@@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
126 .R_WASM_TABLE_INDEX_SLEB,126 .R_WASM_TABLE_INDEX_SLEB,
127 .R_WASM_TABLE_NUMBER_LEB,127 .R_WASM_TABLE_NUMBER_LEB,
128 .R_WASM_TYPE_INDEX_LEB,128 .R_WASM_TYPE_INDEX_LEB,
129 .R_WASM_MEMORY_ADDR_TLS_SLEB,
129 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)),130 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)),
130 .R_WASM_MEMORY_ADDR_LEB64,131 .R_WASM_MEMORY_ADDR_LEB64,
131 .R_WASM_MEMORY_ADDR_SLEB64,132 .R_WASM_MEMORY_ADDR_SLEB64,
132 .R_WASM_TABLE_INDEX_SLEB64,133 .R_WASM_TABLE_INDEX_SLEB64,
134 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
133 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),135 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),
134 }136 }
135 }137 }
...@@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
190 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;192 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
191 return @intCast(u32, rel_value);193 return @intCast(u32, rel_value);
192 },194 },
195 .R_WASM_MEMORY_ADDR_TLS_SLEB,
196 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
197 => {
198 @panic("TODO: Implement TLS relocations");
199 },
193 }200 }
194}201}
src/link/Wasm/Object.zig+39-10
...@@ -601,8 +601,8 @@ fn Parser(comptime ReaderType: type) type {...@@ -601,8 +601,8 @@ fn Parser(comptime ReaderType: type) type {
601 });601 });
602602
603 for (relocations) |*relocation| {603 for (relocations) |*relocation| {
604 const rel_type = try leb.readULEB128(u8, reader);604 const rel_type = try reader.readByte();
605 const rel_type_enum = @intToEnum(types.Relocation.RelocationType, rel_type);605 const rel_type_enum = std.meta.intToEnum(types.Relocation.RelocationType, rel_type) catch return error.MalformedSection;
606 relocation.* = .{606 relocation.* = .{
607 .relocation_type = rel_type_enum,607 .relocation_type = rel_type_enum,
608 .offset = try leb.readULEB128(u32, reader),608 .offset = try leb.readULEB128(u32, reader),
...@@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type {
674 segment.alignment,674 segment.alignment,
675 segment.flags,675 segment.flags,
676 });676 });
677
678 // support legacy object files that specified being TLS by the name instead of the TLS flag.
679 if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {
680 // set the flag so we can simply check for the flag in the rest of the linker.
681 segment.flags |= @enumToInt(types.Segment.Flags.WASM_SEG_FLAG_TLS);
682 }
677 }683 }
678 parser.object.segment_info = segments;684 parser.object.segment_info = segments;
679 },685 },
...@@ -846,12 +852,17 @@ fn readEnum(comptime T: type, reader: anytype) !T {...@@ -846,12 +852,17 @@ fn readEnum(comptime T: type, reader: anytype) !T {
846}852}
847853
848fn readLimits(reader: anytype) !std.wasm.Limits {854fn readLimits(reader: anytype) !std.wasm.Limits {
849 const flags = try readLeb(u1, reader);855 const flags = try reader.readByte();
850 const min = try readLeb(u32, reader);856 const min = try readLeb(u32, reader);
851 return std.wasm.Limits{857 var limits: std.wasm.Limits = .{
858 .flags = flags,
852 .min = min,859 .min = min,
853 .max = if (flags == 0) null else try readLeb(u32, reader),860 .max = undefined,
854 };861 };
862 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
863 limits.max = try readLeb(u32, reader);
864 }
865 return limits;
855}866}
856867
857fn readInit(reader: anytype) !std.wasm.InitExpression {868fn readInit(reader: anytype) !std.wasm.InitExpression {
...@@ -919,11 +930,29 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b...@@ -919,11 +930,29 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
919 reloc.offset -= relocatable_data.offset;930 reloc.offset -= relocatable_data.offset;
920 try atom.relocs.append(gpa, reloc);931 try atom.relocs.append(gpa, reloc);
921932
922 if (relocation.isTableIndex()) {933 switch (relocation.relocation_type) {
923 try wasm_bin.function_table.put(gpa, .{934 .R_WASM_TABLE_INDEX_I32,
924 .file = object_index,935 .R_WASM_TABLE_INDEX_I64,
925 .index = relocation.index,936 .R_WASM_TABLE_INDEX_SLEB,
926 }, 0);937 .R_WASM_TABLE_INDEX_SLEB64,
938 => {
939 try wasm_bin.function_table.put(gpa, .{
940 .file = object_index,
941 .index = relocation.index,
942 }, 0);
943 },
944 .R_WASM_GLOBAL_INDEX_I32,
945 .R_WASM_GLOBAL_INDEX_LEB,
946 => {
947 const sym = object.symtable[relocation.index];
948 if (sym.tag != .global) {
949 try wasm_bin.got_symbols.append(
950 wasm_bin.base.allocator,
951 .{ .file = object_index, .index = relocation.index },
952 );
953 }
954 },
955 else => {},
927 }956 }
928 }957 }
929 }958 }
src/link/Wasm/Symbol.zig+4
...@@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool {...@@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool {
90 return true;90 return true;
91}91}
9292
93pub fn isTLS(symbol: Symbol) bool {
94 return symbol.flags & @enumToInt(Flag.WASM_SYM_TLS) != 0;
95}
96
93pub fn hasFlag(symbol: Symbol, flag: Flag) bool {97pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
94 return symbol.flags & @enumToInt(flag) != 0;98 return symbol.flags & @enumToInt(flag) != 0;
95}99}
src/link/Wasm/types.zig+29-24
...@@ -38,6 +38,8 @@ pub const Relocation = struct {...@@ -38,6 +38,8 @@ pub const Relocation = struct {
38 R_WASM_TABLE_INDEX_SLEB64 = 18,38 R_WASM_TABLE_INDEX_SLEB64 = 18,
39 R_WASM_TABLE_INDEX_I64 = 19,39 R_WASM_TABLE_INDEX_I64 = 19,
40 R_WASM_TABLE_NUMBER_LEB = 20,40 R_WASM_TABLE_NUMBER_LEB = 20,
41 R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
42 R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
4143
42 /// Returns true for relocation types where the `addend` field is present.44 /// Returns true for relocation types where the `addend` field is present.
43 pub fn addendIsPresent(self: RelocationType) bool {45 pub fn addendIsPresent(self: RelocationType) bool {
...@@ -48,6 +50,8 @@ pub const Relocation = struct {...@@ -48,6 +50,8 @@ pub const Relocation = struct {
48 .R_WASM_MEMORY_ADDR_LEB64,50 .R_WASM_MEMORY_ADDR_LEB64,
49 .R_WASM_MEMORY_ADDR_SLEB64,51 .R_WASM_MEMORY_ADDR_SLEB64,
50 .R_WASM_MEMORY_ADDR_I64,52 .R_WASM_MEMORY_ADDR_I64,
53 .R_WASM_MEMORY_ADDR_TLS_SLEB,
54 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
51 .R_WASM_FUNCTION_OFFSET_I32,55 .R_WASM_FUNCTION_OFFSET_I32,
52 .R_WASM_SECTION_OFFSET_I32,56 .R_WASM_SECTION_OFFSET_I32,
53 => true,57 => true,
...@@ -67,18 +71,6 @@ pub const Relocation = struct {...@@ -67,18 +71,6 @@ pub const Relocation = struct {
67 };71 };
68 }72 }
6973
70 /// Returns true when the relocation represents a table index relocatable
71 pub fn isTableIndex(self: Relocation) bool {
72 return switch (self.relocation_type) {
73 .R_WASM_TABLE_INDEX_I32,
74 .R_WASM_TABLE_INDEX_I64,
75 .R_WASM_TABLE_INDEX_SLEB,
76 .R_WASM_TABLE_INDEX_SLEB64,
77 => true,
78 else => false,
79 };
80 }
81
82 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {74 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
83 _ = fmt;75 _ = fmt;
84 _ = options;76 _ = options;
...@@ -125,23 +117,34 @@ pub const Segment = struct {...@@ -125,23 +117,34 @@ pub const Segment = struct {
125 /// Bitfield containing flags for a segment117 /// Bitfield containing flags for a segment
126 flags: u32,118 flags: u32,
127119
120 pub fn isTLS(segment: Segment) bool {
121 return segment.flags & @enumToInt(Flags.WASM_SEG_FLAG_TLS) != 0;
122 }
123
128 /// Returns the name as how it will be output into the final object124 /// Returns the name as how it will be output into the final object
129 /// file or binary. When `merge_segments` is true, this will return the125 /// file or binary. When `merge_segments` is true, this will return the
130 /// short name. i.e. ".rodata". When false, it returns the entire name instead.126 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
131 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {127 pub fn outputName(segment: Segment, merge_segments: bool) []const u8 {
132 if (std.mem.startsWith(u8, self.name, ".synthetic")) return ".synthetic"; // always merge128 if (segment.isTLS()) {
133 if (!merge_segments) return self.name;129 return ".tdata";
134 if (std.mem.startsWith(u8, self.name, ".rodata.")) {130 } else if (!merge_segments) {
131 return segment.name;
132 } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
135 return ".rodata";133 return ".rodata";
136 } else if (std.mem.startsWith(u8, self.name, ".text.")) {134 } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
137 return ".text";135 return ".text";
138 } else if (std.mem.startsWith(u8, self.name, ".data.")) {136 } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
139 return ".data";137 return ".data";
140 } else if (std.mem.startsWith(u8, self.name, ".bss.")) {138 } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
141 return ".bss";139 return ".bss";
142 }140 }
143 return self.name;141 return segment.name;
144 }142 }
143
144 pub const Flags = enum(u32) {
145 WASM_SEG_FLAG_STRINGS = 0x1,
146 WASM_SEG_FLAG_TLS = 0x2,
147 };
145};148};
146149
147pub const InitFunc = struct {150pub const InitFunc = struct {
...@@ -205,8 +208,10 @@ pub const Feature = struct {...@@ -205,8 +208,10 @@ pub const Feature = struct {
205 return @intToEnum(Tag, @enumToInt(feature));208 return @intToEnum(Tag, @enumToInt(feature));
206 }209 }
207210
208 pub fn toString(tag: Tag) []const u8 {211 pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
209 return switch (tag) {212 _ = fmt;
213 _ = opt;
214 try writer.writeAll(switch (tag) {
210 .atomics => "atomics",215 .atomics => "atomics",
211 .bulk_memory => "bulk-memory",216 .bulk_memory => "bulk-memory",
212 .exception_handling => "exception-handling",217 .exception_handling => "exception-handling",
...@@ -220,7 +225,7 @@ pub const Feature = struct {...@@ -220,7 +225,7 @@ pub const Feature = struct {
220 .simd128 => "simd128",225 .simd128 => "simd128",
221 .tail_call => "tail-call",226 .tail_call => "tail-call",
222 .shared_mem => "shared-mem",227 .shared_mem => "shared-mem",
223 };228 });
224 }229 }
225 };230 };
226231
...@@ -233,7 +238,7 @@ pub const Feature = struct {...@@ -233,7 +238,7 @@ pub const Feature = struct {
233 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {238 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
234 _ = opt;239 _ = opt;
235 _ = fmt;240 _ = fmt;
236 try writer.print("{c} {s}", .{ feature.prefix, feature.tag.toString() });241 try writer.print("{c} {}", .{ feature.prefix, feature.tag });
237 }242 }
238};243};
239244