| author | |
| committer | |
| log | 8abdebecdca3a905099fa17f2497e8bf5f918e8a |
| tree | e1a49978c7483cd298bdcac5d747f88dbb728412 |
| parent | b5261599d70dfdb5709a19fabc0e5fe9a3d0e3aa |
7 files changed, 136 insertions(+), 47 deletions(-)
lib/std/wasm.zig+12-1| ... | @@ -655,7 +655,18 @@ pub const function_type: u8 = 0x60; | ... | @@ -655,7 +655,18 @@ pub const function_type: u8 = 0x60; |
| 655 | pub const result_type: u8 = 0x40; | 655 | pub const result_type: u8 = 0x40; |
| 656 | 656 | ||
| 657 | /// Represents a block which will not return a value | 657 | /// Represents a block which will not return a value |
| 658 | pub const block_empty: u8 = 0x40; | 658 | pub const BlockType = enum(u8) { |
| 659 | empty = 0x40, | ||
| 660 | i32 = 0x7F, | ||
| 661 | i64 = 0x7E, | ||
| 662 | f32 = 0x7D, | ||
| 663 | f64 = 0x7C, | ||
| 664 | v128 = 0x7B, | ||
| 665 | |||
| 666 | pub fn fromValtype(valtype: Valtype) BlockType { | ||
| 667 | return @enumFromInt(@intFromEnum(valtype)); | ||
| 668 | } | ||
| 669 | }; | ||
| 659 | 670 | ||
| 660 | // binary constants | 671 | // binary constants |
| 661 | pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm | 672 | pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm |
src/Value.zig+4-4| ... | @@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable { | ... | @@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable { |
| 241 | 241 | ||
| 242 | /// If the value fits in a u64, return it, otherwise null. | 242 | /// If the value fits in a u64, return it, otherwise null. |
| 243 | /// Asserts not undefined. | 243 | /// Asserts not undefined. |
| 244 | pub fn getUnsignedInt(val: Value, zcu: *Zcu) ?u64 { | 244 | pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 { |
| 245 | return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; | 245 | return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | /// Asserts the value is an integer and it fits in a u64 | 248 | /// Asserts the value is an integer and it fits in a u64 |
| 249 | pub fn toUnsignedInt(val: Value, zcu: *Zcu) u64 { | 249 | pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 { |
| 250 | return getUnsignedInt(val, zcu).?; | 250 | return getUnsignedInt(val, zcu).?; |
| 251 | } | 251 | } |
| 252 | 252 | ||
| ... | @@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { | ... | @@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { |
| 259 | pub fn getUnsignedIntInner( | 259 | pub fn getUnsignedIntInner( |
| 260 | val: Value, | 260 | val: Value, |
| 261 | comptime strat: ResolveStrat, | 261 | comptime strat: ResolveStrat, |
| 262 | zcu: *Zcu, | 262 | zcu: strat.ZcuPtr(), |
| 263 | tid: strat.Tid(), | 263 | tid: strat.Tid(), |
| 264 | ) !?u64 { | 264 | ) !?u64 { |
| 265 | return switch (val.toIntern()) { | 265 | return switch (val.toIntern()) { |
| ... | @@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { | ... | @@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { |
| 304 | } | 304 | } |
| 305 | 305 | ||
| 306 | /// Asserts the value is an integer and it fits in a i64 | 306 | /// Asserts the value is an integer and it fits in a i64 |
| 307 | pub fn toSignedInt(val: Value, zcu: *Zcu) i64 { | 307 | pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { |
| 308 | return switch (val.toIntern()) { | 308 | return switch (val.toIntern()) { |
| 309 | .bool_false => 0, | 309 | .bool_false => 0, |
| 310 | .bool_true => 1, | 310 | .bool_true => 1, |
src/arch/wasm/CodeGen.zig+25-30| ... | @@ -80,7 +80,7 @@ start_mir_extra_off: u32, | ... | @@ -80,7 +80,7 @@ start_mir_extra_off: u32, |
| 80 | start_locals_off: u32, | 80 | start_locals_off: u32, |
| 81 | /// List of all locals' types generated throughout this declaration | 81 | /// List of all locals' types generated throughout this declaration |
| 82 | /// used to emit locals count at start of 'code' section. | 82 | /// used to emit locals count at start of 'code' section. |
| 83 | locals: *std.ArrayListUnmanaged(u8), | 83 | locals: *std.ArrayListUnmanaged(std.wasm.Valtype), |
| 84 | /// When a function is executing, we store the the current stack pointer's value within this local. | 84 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 85 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | 85 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 86 | initial_stack_value: WValue = .none, | 86 | initial_stack_value: WValue = .none, |
| ... | @@ -210,7 +210,7 @@ const WValue = union(enum) { | ... | @@ -210,7 +210,7 @@ const WValue = union(enum) { |
| 210 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. | 210 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 211 | 211 | ||
| 212 | const index = local_value - reserved; | 212 | const index = local_value - reserved; |
| 213 | const valtype: std.wasm.Valtype = @enumFromInt(gen.locals.items[gen.start_locals_off + index]); | 213 | const valtype = gen.locals.items[gen.start_locals_off + index]; |
| 214 | switch (valtype) { | 214 | switch (valtype) { |
| 215 | .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead | 215 | .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead |
| 216 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, | 216 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| ... | @@ -995,18 +995,13 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w | ... | @@ -995,18 +995,13 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w |
| 995 | }; | 995 | }; |
| 996 | } | 996 | } |
| 997 | 997 | ||
| 998 | /// Using a given `Type`, returns the byte representation of its wasm value type | ||
| 999 | fn genValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) u8 { | ||
| 1000 | return @intFromEnum(typeToValtype(ty, zcu, target)); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | /// Using a given `Type`, returns the corresponding wasm value type | 998 | /// Using a given `Type`, returns the corresponding wasm value type |
| 1004 | /// Differently from `genValtype` this also allows `void` to create a block | 999 | /// Differently from `typeToValtype` this also allows `void` to create a block |
| 1005 | /// with no return type | 1000 | /// with no return type |
| 1006 | fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) u8 { | 1001 | fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.BlockType { |
| 1007 | return switch (ty.ip_index) { | 1002 | return switch (ty.ip_index) { |
| 1008 | .void_type, .noreturn_type => std.wasm.block_empty, | 1003 | .void_type, .noreturn_type => .empty, |
| 1009 | else => genValtype(ty, zcu, target), | 1004 | else => .fromValtype(typeToValtype(ty, zcu, target)), |
| 1010 | }; | 1005 | }; |
| 1011 | } | 1006 | } |
| 1012 | 1007 | ||
| ... | @@ -1145,7 +1140,7 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -1145,7 +1140,7 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1145 | /// to use a zero-initialized local. | 1140 | /// to use a zero-initialized local. |
| 1146 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | 1141 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1147 | const zcu = cg.pt.zcu; | 1142 | const zcu = cg.pt.zcu; |
| 1148 | try cg.locals.append(cg.gpa, genValtype(ty, zcu, cg.target)); | 1143 | try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); |
| 1149 | const initial_index = cg.local_index; | 1144 | const initial_index = cg.local_index; |
| 1150 | cg.local_index += 1; | 1145 | cg.local_index += 1; |
| 1151 | return .{ .local = .{ .value = initial_index, .references = 1 } }; | 1146 | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| ... | @@ -1197,7 +1192,7 @@ pub const Function = extern struct { | ... | @@ -1197,7 +1192,7 @@ pub const Function = extern struct { |
| 1197 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; | 1192 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; |
| 1198 | for (locals) |local| { | 1193 | for (locals) |local| { |
| 1199 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; | 1194 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; |
| 1200 | code.appendAssumeCapacity(local); | 1195 | code.appendAssumeCapacity(@intFromEnum(local)); |
| 1201 | } | 1196 | } |
| 1202 | 1197 | ||
| 1203 | // Stack management section of function prologue. | 1198 | // Stack management section of function prologue. |
| ... | @@ -1651,8 +1646,8 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1651,8 +1646,8 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1651 | try cg.addLocal(.local_set, offset.local.value); | 1646 | try cg.addLocal(.local_set, offset.local.value); |
| 1652 | 1647 | ||
| 1653 | // outer block to jump to when loop is done | 1648 | // outer block to jump to when loop is done |
| 1654 | try cg.startBlock(.block, std.wasm.block_empty); | 1649 | try cg.startBlock(.block, .empty); |
| 1655 | try cg.startBlock(.loop, std.wasm.block_empty); | 1650 | try cg.startBlock(.loop, .empty); |
| 1656 | 1651 | ||
| 1657 | // loop condition (offset == length -> break) | 1652 | // loop condition (offset == length -> break) |
| 1658 | { | 1653 | { |
| ... | @@ -3387,12 +3382,12 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const | ... | @@ -3387,12 +3382,12 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const |
| 3387 | const wasm_block_ty = genBlockType(block_ty, zcu, cg.target); | 3382 | const wasm_block_ty = genBlockType(block_ty, zcu, cg.target); |
| 3388 | 3383 | ||
| 3389 | // if wasm_block_ty is non-empty, we create a register to store the temporary value | 3384 | // if wasm_block_ty is non-empty, we create a register to store the temporary value |
| 3390 | const block_result: WValue = if (wasm_block_ty != std.wasm.block_empty) blk: { | 3385 | const block_result: WValue = if (wasm_block_ty != .empty) blk: { |
| 3391 | const ty: Type = if (isByRef(block_ty, zcu, cg.target)) Type.u32 else block_ty; | 3386 | const ty: Type = if (isByRef(block_ty, zcu, cg.target)) Type.u32 else block_ty; |
| 3392 | break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten | 3387 | break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten |
| 3393 | } else .none; | 3388 | } else .none; |
| 3394 | 3389 | ||
| 3395 | try cg.startBlock(.block, std.wasm.block_empty); | 3390 | try cg.startBlock(.block, .empty); |
| 3396 | // Here we set the current block idx, so breaks know the depth to jump | 3391 | // Here we set the current block idx, so breaks know the depth to jump |
| 3397 | // to when breaking out. | 3392 | // to when breaking out. |
| 3398 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ | 3393 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ |
| ... | @@ -3410,11 +3405,11 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const | ... | @@ -3410,11 +3405,11 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const |
| 3410 | } | 3405 | } |
| 3411 | 3406 | ||
| 3412 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 | 3407 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 |
| 3413 | fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, valtype: u8) !void { | 3408 | fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, block_type: std.wasm.BlockType) !void { |
| 3414 | cg.block_depth += 1; | 3409 | cg.block_depth += 1; |
| 3415 | try cg.addInst(.{ | 3410 | try cg.addInst(.{ |
| 3416 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), | 3411 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), |
| 3417 | .data = .{ .block_type = valtype }, | 3412 | .data = .{ .block_type = block_type }, |
| 3418 | }); | 3413 | }); |
| 3419 | } | 3414 | } |
| 3420 | 3415 | ||
| ... | @@ -3431,7 +3426,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3431,7 +3426,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3431 | 3426 | ||
| 3432 | // result type of loop is always 'noreturn', meaning we can always | 3427 | // result type of loop is always 'noreturn', meaning we can always |
| 3433 | // emit the wasm type 'block_empty'. | 3428 | // emit the wasm type 'block_empty'. |
| 3434 | try cg.startBlock(.loop, std.wasm.block_empty); | 3429 | try cg.startBlock(.loop, .empty); |
| 3435 | 3430 | ||
| 3436 | try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth); | 3431 | try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth); |
| 3437 | defer assert(cg.loops.remove(inst)); | 3432 | defer assert(cg.loops.remove(inst)); |
| ... | @@ -3451,7 +3446,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3451,7 +3446,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3451 | const liveness_condbr = cg.liveness.getCondBr(inst); | 3446 | const liveness_condbr = cg.liveness.getCondBr(inst); |
| 3452 | 3447 | ||
| 3453 | // result type is always noreturn, so use `block_empty` as type. | 3448 | // result type is always noreturn, so use `block_empty` as type. |
| 3454 | try cg.startBlock(.block, std.wasm.block_empty); | 3449 | try cg.startBlock(.block, .empty); |
| 3455 | // emit the conditional value | 3450 | // emit the conditional value |
| 3456 | try cg.emitWValue(condition); | 3451 | try cg.emitWValue(condition); |
| 3457 | 3452 | ||
| ... | @@ -3940,7 +3935,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3940,7 +3935,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3940 | const pt = cg.pt; | 3935 | const pt = cg.pt; |
| 3941 | const zcu = pt.zcu; | 3936 | const zcu = pt.zcu; |
| 3942 | // result type is always 'noreturn' | 3937 | // result type is always 'noreturn' |
| 3943 | const blocktype = std.wasm.block_empty; | 3938 | const blocktype: std.wasm.BlockType = .empty; |
| 3944 | const switch_br = cg.air.unwrapSwitch(inst); | 3939 | const switch_br = cg.air.unwrapSwitch(inst); |
| 3945 | const target = try cg.resolveInst(switch_br.operand); | 3940 | const target = try cg.resolveInst(switch_br.operand); |
| 3946 | const target_ty = cg.typeOf(switch_br.operand); | 3941 | const target_ty = cg.typeOf(switch_br.operand); |
| ... | @@ -4832,8 +4827,8 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -4832,8 +4827,8 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 4832 | try cg.addLocal(.local_set, end_ptr.local.value); | 4827 | try cg.addLocal(.local_set, end_ptr.local.value); |
| 4833 | 4828 | ||
| 4834 | // outer block to jump to when loop is done | 4829 | // outer block to jump to when loop is done |
| 4835 | try cg.startBlock(.block, std.wasm.block_empty); | 4830 | try cg.startBlock(.block, .empty); |
| 4836 | try cg.startBlock(.loop, std.wasm.block_empty); | 4831 | try cg.startBlock(.loop, .empty); |
| 4837 | 4832 | ||
| 4838 | // check for condition for loop end | 4833 | // check for condition for loop end |
| 4839 | try cg.emitWValue(new_ptr); | 4834 | try cg.emitWValue(new_ptr); |
| ... | @@ -5410,7 +5405,7 @@ fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: st | ... | @@ -5410,7 +5405,7 @@ fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: st |
| 5410 | var result = try cg.ensureAllocLocal(Type.i32); | 5405 | var result = try cg.ensureAllocLocal(Type.i32); |
| 5411 | defer result.free(cg); | 5406 | defer result.free(cg); |
| 5412 | 5407 | ||
| 5413 | try cg.startBlock(.block, std.wasm.block_empty); | 5408 | try cg.startBlock(.block, .empty); |
| 5414 | _ = try cg.isNull(lhs, operand_ty, .i32_eq); | 5409 | _ = try cg.isNull(lhs, operand_ty, .i32_eq); |
| 5415 | _ = try cg.isNull(rhs, operand_ty, .i32_eq); | 5410 | _ = try cg.isNull(rhs, operand_ty, .i32_eq); |
| 5416 | try cg.addTag(.i32_ne); // inverse so we can exit early | 5411 | try cg.addTag(.i32_ne); // inverse so we can exit early |
| ... | @@ -6420,7 +6415,7 @@ fn lowerTry( | ... | @@ -6420,7 +6415,7 @@ fn lowerTry( |
| 6420 | 6415 | ||
| 6421 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { | 6416 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { |
| 6422 | // Block we can jump out of when error is not set | 6417 | // Block we can jump out of when error is not set |
| 6423 | try cg.startBlock(.block, std.wasm.block_empty); | 6418 | try cg.startBlock(.block, .empty); |
| 6424 | 6419 | ||
| 6425 | // check if the error tag is set for the error union. | 6420 | // check if the error tag is set for the error union. |
| 6426 | try cg.emitWValue(err_union); | 6421 | try cg.emitWValue(err_union); |
| ... | @@ -7105,11 +7100,11 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7105,11 +7100,11 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7105 | } | 7100 | } |
| 7106 | 7101 | ||
| 7107 | // start block for 'true' branch | 7102 | // start block for 'true' branch |
| 7108 | try cg.startBlock(.block, std.wasm.block_empty); | 7103 | try cg.startBlock(.block, .empty); |
| 7109 | // start block for 'false' branch | 7104 | // start block for 'false' branch |
| 7110 | try cg.startBlock(.block, std.wasm.block_empty); | 7105 | try cg.startBlock(.block, .empty); |
| 7111 | // block for the jump table itself | 7106 | // block for the jump table itself |
| 7112 | try cg.startBlock(.block, std.wasm.block_empty); | 7107 | try cg.startBlock(.block, .empty); |
| 7113 | 7108 | ||
| 7114 | // lower operand to determine jump table target | 7109 | // lower operand to determine jump table target |
| 7115 | try cg.emitWValue(operand); | 7110 | try cg.emitWValue(operand); |
| ... | @@ -7274,7 +7269,7 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7274,7 +7269,7 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7274 | const value = try tmp.toLocal(cg, ty); | 7269 | const value = try tmp.toLocal(cg, ty); |
| 7275 | 7270 | ||
| 7276 | // create a loop to cmpxchg the new value | 7271 | // create a loop to cmpxchg the new value |
| 7277 | try cg.startBlock(.loop, std.wasm.block_empty); | 7272 | try cg.startBlock(.loop, .empty); |
| 7278 | 7273 | ||
| 7279 | try cg.emitWValue(ptr); | 7274 | try cg.emitWValue(ptr); |
| 7280 | try cg.emitWValue(value); | 7275 | try cg.emitWValue(value); |
src/arch/wasm/Emit.zig+1-1| ... | @@ -43,7 +43,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -43,7 +43,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 43 | const block_type = datas[inst].block_type; | 43 | const block_type = datas[inst].block_type; |
| 44 | try code.ensureUnusedCapacity(gpa, 2); | 44 | try code.ensureUnusedCapacity(gpa, 2); |
| 45 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); | 45 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); |
| 46 | code.appendAssumeCapacity(block_type); | 46 | code.appendAssumeCapacity(@intFromEnum(block_type)); |
| 47 | 47 | ||
| 48 | inst += 1; | 48 | inst += 1; |
| 49 | continue :loop tags[inst]; | 49 | continue :loop tags[inst]; |
src/arch/wasm/Mir.zig+1-1| ... | @@ -588,7 +588,7 @@ pub const Inst = struct { | ... | @@ -588,7 +588,7 @@ pub const Inst = struct { |
| 588 | /// Uses no additional data | 588 | /// Uses no additional data |
| 589 | tag: void, | 589 | tag: void, |
| 590 | /// Contains the result type of a block | 590 | /// Contains the result type of a block |
| 591 | block_type: u8, | 591 | block_type: std.wasm.BlockType, |
| 592 | /// Label: Each structured control instruction introduces an implicit label. | 592 | /// Label: Each structured control instruction introduces an implicit label. |
| 593 | /// Labels are targets for branch instructions that reference them with | 593 | /// Labels are targets for branch instructions that reference them with |
| 594 | /// label indices. Unlike with other index spaces, indexing of labels | 594 | /// label indices. Unlike with other index spaces, indexing of labels |
src/link/Wasm.zig+1-1| ... | @@ -283,7 +283,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ... | @@ -283,7 +283,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 283 | /// Corresponds to `mir_instructions`. | 283 | /// Corresponds to `mir_instructions`. |
| 284 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | 284 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, |
| 285 | /// All local types for all Zcu functions. | 285 | /// All local types for all Zcu functions. |
| 286 | all_zcu_locals: std.ArrayListUnmanaged(u8) = .empty, | 286 | all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| 287 | 287 | ||
| 288 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | 288 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| 289 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | 289 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
src/link/Wasm/Flush.zig+92-9| ... | @@ -780,9 +780,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -780,9 +780,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 780 | 780 | ||
| 781 | const zcu = comp.zcu.?; | 781 | const zcu = comp.zcu.?; |
| 782 | const ip = &zcu.intern_pool; | 782 | const ip = &zcu.intern_pool; |
| 783 | switch (ip.indexToKey(i.key(wasm).*)) { | 783 | const ip_index = i.key(wasm).*; |
| 784 | switch (ip.indexToKey(ip_index)) { | ||
| 784 | .enum_type => { | 785 | .enum_type => { |
| 785 | try emitTagNameFunction(gpa, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index); | 786 | try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); |
| 786 | }, | 787 | }, |
| 787 | else => try i.value(wasm).function.lower(wasm, binary_bytes), | 788 | else => try i.value(wasm).function.lower(wasm, binary_bytes), |
| 788 | } | 789 | } |
| ... | @@ -1772,13 +1773,13 @@ fn emitInitMemoryFunction( | ... | @@ -1772,13 +1773,13 @@ fn emitInitMemoryFunction( |
| 1772 | // destination blocks | 1773 | // destination blocks |
| 1773 | // based on values we jump to corresponding label | 1774 | // based on values we jump to corresponding label |
| 1774 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop | 1775 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop |
| 1775 | binary_bytes.appendAssumeCapacity(std.wasm.block_empty); // block type | 1776 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1776 | 1777 | ||
| 1777 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait | 1778 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait |
| 1778 | binary_bytes.appendAssumeCapacity(std.wasm.block_empty); // block type | 1779 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1779 | 1780 | ||
| 1780 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init | 1781 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init |
| 1781 | binary_bytes.appendAssumeCapacity(std.wasm.block_empty); // block type | 1782 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1782 | 1783 | ||
| 1783 | // atomically check | 1784 | // atomically check |
| 1784 | appendReservedI32Const(binary_bytes, flag_address); | 1785 | appendReservedI32Const(binary_bytes, flag_address); |
| ... | @@ -1898,18 +1899,25 @@ fn emitInitMemoryFunction( | ... | @@ -1898,18 +1899,25 @@ fn emitInitMemoryFunction( |
| 1898 | } | 1899 | } |
| 1899 | 1900 | ||
| 1900 | fn emitTagNameFunction( | 1901 | fn emitTagNameFunction( |
| 1901 | gpa: Allocator, | 1902 | wasm: *Wasm, |
| 1902 | code: *std.ArrayListUnmanaged(u8), | 1903 | code: *std.ArrayListUnmanaged(u8), |
| 1903 | table_base_addr: u32, | 1904 | table_base_addr: u32, |
| 1904 | table_index: u32, | 1905 | table_index: u32, |
| 1905 | ) Allocator.Error!void { | 1906 | enum_type_ip: InternPool.Index, |
| 1907 | ) !void { | ||
| 1908 | const comp = wasm.base.comp; | ||
| 1909 | const gpa = comp.gpa; | ||
| 1910 | const diags = &comp.link_diags; | ||
| 1911 | const zcu = comp.zcu.?; | ||
| 1912 | const ip = &zcu.intern_pool; | ||
| 1913 | const enum_type = ip.loadEnumType(enum_type_ip); | ||
| 1914 | |||
| 1906 | try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6); | 1915 | try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6); |
| 1907 | appendReservedUleb32(code, 0); // no locals | 1916 | appendReservedUleb32(code, 0); // no locals |
| 1908 | 1917 | ||
| 1909 | const slice_abi_size = 8; | 1918 | const slice_abi_size = 8; |
| 1910 | const encoded_alignment = @ctz(@as(u32, 4)); | 1919 | const encoded_alignment = @ctz(@as(u32, 4)); |
| 1911 | const all_tag_values_autoassigned = true; | 1920 | if (enum_type.tag_mode == .auto) { |
| 1912 | if (all_tag_values_autoassigned) { | ||
| 1913 | // Then it's a direct table lookup. | 1921 | // Then it's a direct table lookup. |
| 1914 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | 1922 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1915 | appendReservedUleb32(code, 0); | 1923 | appendReservedUleb32(code, 0); |
| ... | @@ -1924,6 +1932,75 @@ fn emitTagNameFunction( | ... | @@ -1924,6 +1932,75 @@ fn emitTagNameFunction( |
| 1924 | appendReservedUleb32(code, encoded_alignment); | 1932 | appendReservedUleb32(code, encoded_alignment); |
| 1925 | appendReservedUleb32(code, table_base_addr + table_index * 8); | 1933 | appendReservedUleb32(code, table_base_addr + table_index * 8); |
| 1926 | 1934 | ||
| 1935 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); | ||
| 1936 | appendReservedUleb32(code, encoded_alignment); | ||
| 1937 | appendReservedUleb32(code, 0); | ||
| 1938 | } else { | ||
| 1939 | const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu); | ||
| 1940 | const outer_block_type: std.wasm.BlockType = switch (int_info.bits) { | ||
| 1941 | 0...32 => .i32, | ||
| 1942 | 33...64 => .i64, | ||
| 1943 | else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}), | ||
| 1944 | }; | ||
| 1945 | |||
| 1946 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1947 | appendReservedUleb32(code, 0); | ||
| 1948 | |||
| 1949 | // Outer block that computes table offset. | ||
| 1950 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); | ||
| 1951 | code.appendAssumeCapacity(@intFromEnum(outer_block_type)); | ||
| 1952 | |||
| 1953 | for (enum_type.values.get(ip), 0..) |tag_value, tag_index| { | ||
| 1954 | // block for this if case | ||
| 1955 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); | ||
| 1956 | code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); | ||
| 1957 | |||
| 1958 | // Tag value whose name should be returned. | ||
| 1959 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1960 | appendReservedUleb32(code, 1); | ||
| 1961 | |||
| 1962 | const val: Zcu.Value = .fromInterned(tag_value); | ||
| 1963 | switch (outer_block_type) { | ||
| 1964 | .i32 => { | ||
| 1965 | const x: u32 = switch (int_info.signedness) { | ||
| 1966 | .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))), | ||
| 1967 | .unsigned => @intCast(val.toUnsignedInt(zcu)), | ||
| 1968 | }; | ||
| 1969 | appendReservedI32Const(code, x); | ||
| 1970 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne)); | ||
| 1971 | }, | ||
| 1972 | .i64 => { | ||
| 1973 | const x: u64 = switch (int_info.signedness) { | ||
| 1974 | .signed => @bitCast(val.toSignedInt(zcu)), | ||
| 1975 | .unsigned => val.toUnsignedInt(zcu), | ||
| 1976 | }; | ||
| 1977 | appendReservedI64Const(code, x); | ||
| 1978 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne)); | ||
| 1979 | }, | ||
| 1980 | else => unreachable, | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | // if they're not equal, break out of current branch | ||
| 1984 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if)); | ||
| 1985 | appendReservedUleb32(code, 0); | ||
| 1986 | |||
| 1987 | // Put the table offset of the result on the stack. | ||
| 1988 | appendReservedI32Const(code, @intCast(tag_index * slice_abi_size)); | ||
| 1989 | |||
| 1990 | // break outside blocks | ||
| 1991 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br)); | ||
| 1992 | appendReservedUleb32(code, 1); | ||
| 1993 | |||
| 1994 | // end the block for this case | ||
| 1995 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1996 | } | ||
| 1997 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable")); | ||
| 1998 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1999 | |||
| 2000 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load)); | ||
| 2001 | appendReservedUleb32(code, encoded_alignment); | ||
| 2002 | appendReservedUleb32(code, table_base_addr + table_index * 8); | ||
| 2003 | |||
| 1927 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); | 2004 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); |
| 1928 | appendReservedUleb32(code, encoded_alignment); | 2005 | appendReservedUleb32(code, encoded_alignment); |
| 1929 | appendReservedUleb32(code, 0); | 2006 | appendReservedUleb32(code, 0); |
| ... | @@ -1939,6 +2016,12 @@ fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { | ... | @@ -1939,6 +2016,12 @@ fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { |
| 1939 | leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable; | 2016 | leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable; |
| 1940 | } | 2017 | } |
| 1941 | 2018 | ||
| 2019 | /// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value. | ||
| 2020 | fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void { | ||
| 2021 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); | ||
| 2022 | leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable; | ||
| 2023 | } | ||
| 2024 | |||
| 1942 | fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { | 2025 | fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { |
| 1943 | leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; | 2026 | leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; |
| 1944 | } | 2027 | } |