| ... | @@ -605,7 +605,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -605,7 +605,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 605 | } | 605 | } |
| 606 | }, | 606 | }, |
| 607 | .bit_cast_safe => if (l.features.has(.expand_bit_cast_safe)) { | 607 | .bit_cast_safe => if (l.features.has(.expand_bit_cast_safe)) { |
| 608 | continue :inst l.replaceInst(inst, .block, try l.safeBitcastBlockPayload(inst)); | 608 | if (try l.safeBitcastBlockPayload(inst)) |payload| { |
| | 609 | continue :inst l.replaceInst(inst, .block, payload); |
| | 610 | } |
| | 611 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 612 | continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = ty_op }); |
| 609 | } else if (l.features.hasAny(&.{ | 613 | } else if (l.features.hasAny(&.{ |
| 610 | .scalarize_bit_cast_array, | 614 | .scalarize_bit_cast_array, |
| 611 | .scalarize_bit_cast_vector_non_elementwise, | 615 | .scalarize_bit_cast_vector_non_elementwise, |
| ... | @@ -617,7 +621,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -617,7 +621,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 617 | }, | 621 | }, |
| 618 | .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) { | 622 | .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) { |
| 619 | assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both | 623 | assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both |
| 620 | continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst)); | 624 | if (try l.safeIntcastBlockPayload(inst)) |payload| { |
| | 625 | continue :inst l.replaceInst(inst, .block, payload); |
| | 626 | } |
| | 627 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 628 | continue :inst l.replaceInst(inst, .int_cast, .{ .ty_op = ty_op }); |
| 621 | } else if (l.features.has(.scalarize_int_cast_safe)) { | 629 | } else if (l.features.has(.scalarize_int_cast_safe)) { |
| 622 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | 630 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 623 | if (ty_op.ty.toType().isVector(zcu)) { | 631 | if (ty_op.ty.toType().isVector(zcu)) { |
| ... | @@ -2125,7 +2133,7 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize | ... | @@ -2125,7 +2133,7 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize |
| 2125 | } }; | 2133 | } }; |
| 2126 | } | 2134 | } |
| 2127 | | 2135 | |
| 2128 | fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { | 2136 | fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data { |
| 2129 | const pt = l.pt; | 2137 | const pt = l.pt; |
| 2130 | const zcu = pt.zcu; | 2138 | const zcu = pt.zcu; |
| 2131 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; | 2139 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; |
| ... | @@ -2133,7 +2141,14 @@ fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2133,7 +2141,14 @@ fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2133 | const operand_ref = ty_op.operand; | 2141 | const operand_ref = ty_op.operand; |
| 2134 | const dest_ty = ty_op.ty.toType(); | 2142 | const dest_ty = ty_op.ty.toType(); |
| 2135 | | 2143 | |
| 2136 | // The worst case is a bitcast to an exhaustive enum and looks like this: | 2144 | if (dest_ty.zigTypeTag(zcu) != .@"enum" or |
| | 2145 | dest_ty.isNonexhaustiveEnum(zcu) or |
| | 2146 | !zcu.backendSupportsFeature(.is_named_enum_value)) |
| | 2147 | { |
| | 2148 | return null; |
| | 2149 | } |
| | 2150 | |
| | 2151 | // We are building this: |
| 2137 | // | 2152 | // |
| 2138 | // %x = block({ | 2153 | // %x = block({ |
| 2139 | // %1 = bit_cast(@res_ty, %y) | 2154 | // %1 = bit_cast(@res_ty, %y) |
| ... | @@ -2148,54 +2163,32 @@ fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2148,54 +2163,32 @@ fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2148 | | 2163 | |
| 2149 | var inst_buf: [6]Air.Inst.Index = undefined; | 2164 | var inst_buf: [6]Air.Inst.Index = undefined; |
| 2150 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); | 2165 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| 2151 | var opt_condbr: ?CondBr = null; | | |
| 2152 | | | |
| 2153 | var main_block: Block = .init(&inst_buf); | | |
| 2154 | var cur_block: *Block = &main_block; | | |
| 2155 | | 2166 | |
| 2156 | const cast_inst = cur_block.addBitCast(l, dest_ty, operand_ref); | 2167 | var block: Block = .init(&inst_buf); |
| 2157 | | 2168 | |
| 2158 | if (dest_ty.zigTypeTag(zcu) == .@"enum" and | 2169 | const cast_inst = block.addBitCast(l, dest_ty, operand_ref); |
| 2159 | !dest_ty.isNonexhaustiveEnum(zcu) and | 2170 | const is_named_inst = block.add(l, .{ |
| 2160 | zcu.backendSupportsFeature(.is_named_enum_value)) | 2171 | .tag = .is_named_enum_value, |
| 2161 | { | 2172 | .data = .{ .un_op = cast_inst }, |
| 2162 | // We are building this: | | |
| 2163 | // %1 = is_named_enum_value(%cast_inst) | | |
| 2164 | // %2 = cond_br(%1, { | | |
| 2165 | // <new cursor> | | |
| 2166 | // }, { | | |
| 2167 | // <panic> | | |
| 2168 | // }) | | |
| 2169 | const is_named_inst = cur_block.add(l, .{ | | |
| 2170 | .tag = .is_named_enum_value, | | |
| 2171 | .data = .{ .un_op = cast_inst }, | | |
| 2172 | }); | | |
| 2173 | opt_condbr = .init(l, is_named_inst.toRef(), cur_block, .{ .false = .cold }); | | |
| 2174 | const condbr = &(opt_condbr.?); | | |
| 2175 | condbr.else_block = .init(cur_block.stealRemainingCapacity()); | | |
| 2176 | try condbr.else_block.addPanic(l, .invalid_enum_value); | | |
| 2177 | condbr.then_block = .init(condbr.else_block.stealRemainingCapacity()); | | |
| 2178 | cur_block = &condbr.then_block; | | |
| 2179 | } | | |
| 2180 | // Finally, just `br` to our outer `block`. | | |
| 2181 | _ = cur_block.add(l, .{ | | |
| 2182 | .tag = .br, | | |
| 2183 | .data = .{ .br = .{ | | |
| 2184 | .block_inst = orig_inst, | | |
| 2185 | .operand = cast_inst, | | |
| 2186 | } }, | | |
| 2187 | }); | 2173 | }); |
| 2188 | // We might not have used all of the instructions; that's intentional. | | |
| 2189 | _ = cur_block.stealRemainingCapacity(); | | |
| 2190 | | 2174 | |
| 2191 | if (opt_condbr) |condbr| try condbr.finish(l); | 2175 | var condbr: CondBr = .init(l, is_named_inst.toRef(), &block, .{ .false = .cold }); |
| | 2176 | |
| | 2177 | condbr.then_block = .init(block.stealRemainingCapacity()); |
| | 2178 | condbr.then_block.addBr(l, orig_inst, cast_inst); |
| | 2179 | |
| | 2180 | condbr.else_block = .init(condbr.then_block.stealRemainingCapacity()); |
| | 2181 | try condbr.else_block.addPanic(l, .invalid_enum_value); |
| | 2182 | |
| | 2183 | try condbr.finish(l); |
| | 2184 | |
| 2192 | return .{ .ty_pl = .{ | 2185 | return .{ .ty_pl = .{ |
| 2193 | .ty = .fromType(dest_ty), | 2186 | .ty = .fromType(dest_ty), |
| 2194 | .payload = try l.addBlockBody(main_block.body()), | 2187 | .payload = try l.addBlockBody(block.body()), |
| 2195 | } }; | 2188 | } }; |
| 2196 | } | 2189 | } |
| 2197 | | 2190 | |
| 2198 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { | 2191 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data { |
| 2199 | const pt = l.pt; | 2192 | const pt = l.pt; |
| 2200 | const zcu = pt.zcu; | 2193 | const zcu = pt.zcu; |
| 2201 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; | 2194 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; |
| ... | @@ -2214,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2214,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2214 | .@"enum" => true, | 2207 | .@"enum" => true, |
| 2215 | else => unreachable, | 2208 | else => unreachable, |
| 2216 | }; | 2209 | }; |
| | 2210 | const have_enum_value_check = dest_is_enum and |
| | 2211 | !dest_ty.isNonexhaustiveEnum(zcu) and |
| | 2212 | zcu.backendSupportsFeature(.is_named_enum_value); |
| 2217 | | 2213 | |
| 2218 | const operand_info = operand_scalar_ty.intInfo(zcu); | 2214 | const operand_info = operand_scalar_ty.intInfo(zcu); |
| 2219 | const dest_info = dest_scalar_ty.intInfo(zcu); | 2215 | const dest_info = dest_scalar_ty.intInfo(zcu); |
| ... | @@ -2229,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2229,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2229 | }; | 2225 | }; |
| 2230 | }; | 2226 | }; |
| 2231 | | 2227 | |
| | 2228 | if (!have_enum_value_check and !have_min_check and !have_max_check) { |
| | 2229 | return null; |
| | 2230 | } |
| | 2231 | |
| 2232 | // The worst-case scenario in terms of total instructions and total condbrs is the case where | 2232 | // The worst-case scenario in terms of total instructions and total condbrs is the case where |
| 2233 | // the result type is an exhaustive enum whose tag type is smaller than the operand type: | 2233 | // the result type is an exhaustive enum whose tag type is smaller than the operand type: |
| 2234 | // | 2234 | // |
| ... | @@ -2324,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2324,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2324 | } }, | 2324 | } }, |
| 2325 | }); | 2325 | }); |
| 2326 | // For ints we're already done, but for exhaustive enums we must check this is a valid tag. | 2326 | // For ints we're already done, but for exhaustive enums we must check this is a valid tag. |
| 2327 | if (dest_is_enum and !dest_ty.isNonexhaustiveEnum(zcu) and zcu.backendSupportsFeature(.is_named_enum_value)) { | 2327 | if (have_enum_value_check) { |
| 2328 | assert(!is_vector); // vectors of enums don't exist | 2328 | assert(!is_vector); // vectors of enums don't exist |
| 2329 | // We are building this: | 2329 | // We are building this: |
| 2330 | // %1 = is_named_enum_value(%cast_inst) | 2330 | // %1 = is_named_enum_value(%cast_inst) |
| ... | @@ -2346,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2346,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2346 | cur_block = &condbr.then_block; | 2346 | cur_block = &condbr.then_block; |
| 2347 | } | 2347 | } |
| 2348 | // Finally, just `br` to our outer `block`. | 2348 | // Finally, just `br` to our outer `block`. |
| 2349 | _ = cur_block.add(l, .{ | 2349 | cur_block.addBr(l, orig_inst, cast_inst.toRef()); |
| 2350 | .tag = .br, | 2350 | |
| 2351 | .data = .{ .br = .{ | | |
| 2352 | .block_inst = orig_inst, | | |
| 2353 | .operand = cast_inst.toRef(), | | |
| 2354 | } }, | | |
| 2355 | }); | | |
| 2356 | // We might not have used all of the instructions; that's intentional. | 2351 | // We might not have used all of the instructions; that's intentional. |
| 2357 | _ = cur_block.stealRemainingCapacity(); | 2352 | _ = cur_block.stealRemainingCapacity(); |
| 2358 | | 2353 | |
| | 2354 | assert(condbr_idx != 0); // should have already returned `null` |
| 2359 | for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l); | 2355 | for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l); |
| 2360 | return .{ .ty_pl = .{ | 2356 | return .{ .ty_pl = .{ |
| 2361 | .ty = Air.internedToRef(dest_ty.toIntern()), | 2357 | .ty = Air.internedToRef(dest_ty.toIntern()), |