| ... | @@ -662,6 +662,8 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { | ... | @@ -662,6 +662,8 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { |
| 662 | label: u32, | 662 | label: u32, |
| 663 | value: WValue, | 663 | value: WValue, |
| 664 | }) = .{}, | 664 | }) = .{}, |
| | 665 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. |
| | 666 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .{}, |
| 665 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. | 667 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. |
| 666 | code: *ArrayList(u8), | 668 | code: *ArrayList(u8), |
| 667 | /// The index the next local generated will have | 669 | /// The index the next local generated will have |
| ... | @@ -751,6 +753,7 @@ pub fn deinit(func: *CodeGen) void { | ... | @@ -751,6 +753,7 @@ pub fn deinit(func: *CodeGen) void { |
| 751 | } | 753 | } |
| 752 | func.branches.deinit(func.gpa); | 754 | func.branches.deinit(func.gpa); |
| 753 | func.blocks.deinit(func.gpa); | 755 | func.blocks.deinit(func.gpa); |
| | 756 | func.loops.deinit(func.gpa); |
| 754 | func.locals.deinit(func.gpa); | 757 | func.locals.deinit(func.gpa); |
| 755 | func.simd_immediates.deinit(func.gpa); | 758 | func.simd_immediates.deinit(func.gpa); |
| 756 | func.mir_instructions.deinit(func.gpa); | 759 | func.mir_instructions.deinit(func.gpa); |
| ... | @@ -1903,7 +1906,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1903,7 +1906,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1903 | .trap => func.airTrap(inst), | 1906 | .trap => func.airTrap(inst), |
| 1904 | .breakpoint => func.airBreakpoint(inst), | 1907 | .breakpoint => func.airBreakpoint(inst), |
| 1905 | .br => func.airBr(inst), | 1908 | .br => func.airBr(inst), |
| 1906 | .repeat => return func.fail("TODO implement `repeat`", .{}), | 1909 | .repeat => func.airRepeat(inst), |
| 1907 | .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}), | 1910 | .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}), |
| 1908 | .int_from_bool => func.airIntFromBool(inst), | 1911 | .int_from_bool => func.airIntFromBool(inst), |
| 1909 | .cond_br => func.airCondBr(inst), | 1912 | .cond_br => func.airCondBr(inst), |
| ... | @@ -3537,10 +3540,11 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3537,10 +3540,11 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3537 | // result type of loop is always 'noreturn', meaning we can always | 3540 | // result type of loop is always 'noreturn', meaning we can always |
| 3538 | // emit the wasm type 'block_empty'. | 3541 | // emit the wasm type 'block_empty'. |
| 3539 | try func.startBlock(.loop, wasm.block_empty); | 3542 | try func.startBlock(.loop, wasm.block_empty); |
| 3540 | try func.genBody(body); | | |
| 3541 | | 3543 | |
| 3542 | // breaking to the index of a loop block will continue the loop instead | 3544 | try func.loops.putNoClobber(func.gpa, inst, func.block_depth); |
| 3543 | try func.addLabel(.br, 0); | 3545 | defer assert(func.loops.remove(inst)); |
| | 3546 | |
| | 3547 | try func.genBody(body); |
| 3544 | try func.endBlock(); | 3548 | try func.endBlock(); |
| 3545 | | 3549 | |
| 3546 | return func.finishAir(inst, .none, &.{}); | 3550 | return func.finishAir(inst, .none, &.{}); |
| ... | @@ -3737,6 +3741,16 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3737,6 +3741,16 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3737 | return func.finishAir(inst, .none, &.{br.operand}); | 3741 | return func.finishAir(inst, .none, &.{br.operand}); |
| 3738 | } | 3742 | } |
| 3739 | | 3743 | |
| | 3744 | fn airRepeat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| | 3745 | const repeat = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat; |
| | 3746 | const loop_label = func.loops.get(repeat.loop_inst).?; |
| | 3747 | |
| | 3748 | const idx: u32 = func.block_depth - loop_label; |
| | 3749 | try func.addLabel(.br, idx); |
| | 3750 | |
| | 3751 | return func.finishAir(inst, .none, &.{}); |
| | 3752 | } |
| | 3753 | |
| 3740 | fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3754 | fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3741 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3755 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3742 | | 3756 | |
| ... | @@ -4053,7 +4067,10 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4053,7 +4067,10 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4053 | defer func.gpa.free(liveness.deaths); | 4067 | defer func.gpa.free(liveness.deaths); |
| 4054 | | 4068 | |
| 4055 | // a list that maps each value with its value and body based on the order inside the list. | 4069 | // a list that maps each value with its value and body based on the order inside the list. |
| 4056 | const CaseValue = struct { integer: i32, value: Value }; | 4070 | const CaseValue = union(enum) { |
| | 4071 | singular: struct { integer: i32, value: Value }, |
| | 4072 | range: struct { min: i32, min_value: Value, max: i32, max_value: Value }, |
| | 4073 | }; |
| 4057 | var case_list = try std.ArrayList(struct { | 4074 | var case_list = try std.ArrayList(struct { |
| 4058 | values: []const CaseValue, | 4075 | values: []const CaseValue, |
| 4059 | body: []const Air.Inst.Index, | 4076 | body: []const Air.Inst.Index, |
| ... | @@ -4064,12 +4081,9 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4064,12 +4081,9 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4064 | | 4081 | |
| 4065 | var lowest_maybe: ?i32 = null; | 4082 | var lowest_maybe: ?i32 = null; |
| 4066 | var highest_maybe: ?i32 = null; | 4083 | var highest_maybe: ?i32 = null; |
| 4067 | | | |
| 4068 | var it = switch_br.iterateCases(); | 4084 | var it = switch_br.iterateCases(); |
| 4069 | while (it.next()) |case| { | 4085 | while (it.next()) |case| { |
| 4070 | if (case.ranges.len > 0) return func.fail("TODO: switch with ranges", .{}); | 4086 | const values = try func.gpa.alloc(CaseValue, case.items.len + case.ranges.len); |
| 4071 | | | |
| 4072 | const values = try func.gpa.alloc(CaseValue, case.items.len); | | |
| 4073 | errdefer func.gpa.free(values); | 4087 | errdefer func.gpa.free(values); |
| 4074 | | 4088 | |
| 4075 | for (case.items, 0..) |ref, i| { | 4089 | for (case.items, 0..) |ref, i| { |
| ... | @@ -4081,7 +4095,30 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4081,7 +4095,30 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4081 | if (highest_maybe == null or int_val > highest_maybe.?) { | 4095 | if (highest_maybe == null or int_val > highest_maybe.?) { |
| 4082 | highest_maybe = int_val; | 4096 | highest_maybe = int_val; |
| 4083 | } | 4097 | } |
| 4084 | values[i] = .{ .integer = int_val, .value = item_val }; | 4098 | values[i] = .{ .singular = .{ .integer = int_val, .value = item_val } }; |
| | 4099 | } |
| | 4100 | |
| | 4101 | for (case.ranges, 0..) |range, i| { |
| | 4102 | const min_val = (try func.air.value(range[0], pt)).?; |
| | 4103 | const int_min_val = func.valueAsI32(min_val); |
| | 4104 | |
| | 4105 | if (lowest_maybe == null or int_min_val < lowest_maybe.?) { |
| | 4106 | lowest_maybe = int_min_val; |
| | 4107 | } |
| | 4108 | |
| | 4109 | const max_val = (try func.air.value(range[1], pt)).?; |
| | 4110 | const int_max_val = func.valueAsI32(max_val); |
| | 4111 | |
| | 4112 | if (highest_maybe == null or int_max_val > highest_maybe.?) { |
| | 4113 | highest_maybe = int_max_val; |
| | 4114 | } |
| | 4115 | |
| | 4116 | values[i + case.items.len] = .{ .range = .{ |
| | 4117 | .min = int_min_val, |
| | 4118 | .min_value = min_val, |
| | 4119 | .max = int_max_val, |
| | 4120 | .max_value = max_val, |
| | 4121 | } }; |
| 4085 | } | 4122 | } |
| 4086 | | 4123 | |
| 4087 | case_list.appendAssumeCapacity(.{ .values = values, .body = case.body }); | 4124 | case_list.appendAssumeCapacity(.{ .values = values, .body = case.body }); |
| ... | @@ -4134,7 +4171,12 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4134,7 +4171,12 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4134 | const idx = blk: { | 4171 | const idx = blk: { |
| 4135 | for (case_list.items, 0..) |case, idx| { | 4172 | for (case_list.items, 0..) |case, idx| { |
| 4136 | for (case.values) |case_value| { | 4173 | for (case.values) |case_value| { |
| 4137 | if (case_value.integer == value) break :blk @as(u32, @intCast(idx)); | 4174 | switch (case_value) { |
| | 4175 | .singular => |val| if (val.integer == value) break :blk @as(u32, @intCast(idx)), |
| | 4176 | .range => |range_val| if (value >= range_val.min and value <= range_val.max) { |
| | 4177 | break :blk @as(u32, @intCast(idx)); |
| | 4178 | }, |
| | 4179 | } |
| 4138 | } | 4180 | } |
| 4139 | } | 4181 | } |
| 4140 | // error sets are almost always sparse so we use the default case | 4182 | // error sets are almost always sparse so we use the default case |
| ... | @@ -4150,43 +4192,34 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4150,43 +4192,34 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4150 | try func.endBlock(); | 4192 | try func.endBlock(); |
| 4151 | } | 4193 | } |
| 4152 | | 4194 | |
| 4153 | const signedness: std.builtin.Signedness = blk: { | | |
| 4154 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | | |
| 4155 | if (target_ty.zigTypeTag(zcu) != .int) break :blk .unsigned; | | |
| 4156 | | | |
| 4157 | // incase of an actual integer, we emit the correct signedness | | |
| 4158 | break :blk target_ty.intInfo(zcu).signedness; | | |
| 4159 | }; | | |
| 4160 | | | |
| 4161 | try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @intFromBool(has_else_body)); | 4195 | try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @intFromBool(has_else_body)); |
| 4162 | for (case_list.items, 0..) |case, index| { | 4196 | for (case_list.items, 0..) |case, index| { |
| 4163 | // when sparse, we use if/else-chain, so emit conditional checks | 4197 | // when sparse, we use if/else-chain, so emit conditional checks |
| 4164 | if (is_sparse) { | 4198 | if (is_sparse) { |
| 4165 | // for single value prong we can emit a simple if | 4199 | // for single value prong we can emit a simple condition |
| 4166 | if (case.values.len == 1) { | 4200 | if (case.values.len == 1 and case.values[0] == .singular) { |
| 4167 | try func.emitWValue(target); | 4201 | const val = try func.lowerConstant(case.values[0].singular.value, target_ty); |
| 4168 | const val = try func.lowerConstant(case.values[0].value, target_ty); | 4202 | // not equal, because we want to jump out of this block if it does not match the condition. |
| 4169 | try func.emitWValue(val); | 4203 | _ = try func.cmp(target, val, target_ty, .neq); |
| 4170 | const opcode = buildOpcode(.{ | | |
| 4171 | .valtype1 = typeToValtype(target_ty, pt, func.target.*), | | |
| 4172 | .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. | | |
| 4173 | .signedness = signedness, | | |
| 4174 | }); | | |
| 4175 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | | |
| 4176 | try func.addLabel(.br_if, 0); | 4204 | try func.addLabel(.br_if, 0); |
| 4177 | } else { | 4205 | } else { |
| 4178 | // in multi-value prongs we must check if any prongs match the target value. | 4206 | // in multi-value prongs we must check if any prongs match the target value. |
| 4179 | try func.startBlock(.block, blocktype); | 4207 | try func.startBlock(.block, blocktype); |
| 4180 | for (case.values) |value| { | 4208 | for (case.values) |value| { |
| 4181 | try func.emitWValue(target); | 4209 | switch (value) { |
| 4182 | const val = try func.lowerConstant(value.value, target_ty); | 4210 | .singular => |single_val| { |
| 4183 | try func.emitWValue(val); | 4211 | const val = try func.lowerConstant(single_val.value, target_ty); |
| 4184 | const opcode = buildOpcode(.{ | 4212 | _ = try func.cmp(target, val, target_ty, .eq); |
| 4185 | .valtype1 = typeToValtype(target_ty, pt, func.target.*), | 4213 | }, |
| 4186 | .op = .eq, | 4214 | .range => |range| { |
| 4187 | .signedness = signedness, | 4215 | const min_val = try func.lowerConstant(range.min_value, target_ty); |
| 4188 | }); | 4216 | const max_val = try func.lowerConstant(range.max_value, target_ty); |
| 4189 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 4217 | |
| | 4218 | const gte = try func.cmp(target, min_val, target_ty, .gte); |
| | 4219 | const lte = try func.cmp(target, max_val, target_ty, .lte); |
| | 4220 | _ = try func.binOp(gte, lte, Type.bool, .@"and"); |
| | 4221 | }, |
| | 4222 | } |
| 4190 | try func.addLabel(.br_if, 0); | 4223 | try func.addLabel(.br_if, 0); |
| 4191 | } | 4224 | } |
| 4192 | // value did not match any of the prong values | 4225 | // value did not match any of the prong values |