| ... | ... | @@ -605,7 +605,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 605 | 605 | } |
| 606 | 606 | }, |
| 607 | 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 | 613 | } else if (l.features.hasAny(&.{ |
| 610 | 614 | .scalarize_bit_cast_array, |
| 611 | 615 | .scalarize_bit_cast_vector_non_elementwise, |
| ... | ... | @@ -617,7 +621,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 617 | 621 | }, |
| 618 | 622 | .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) { |
| 619 | 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 | 629 | } else if (l.features.has(.scalarize_int_cast_safe)) { |
| 622 | 630 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 623 | 631 | if (ty_op.ty.toType().isVector(zcu)) { |
| ... | ... | @@ -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 | 2137 | const pt = l.pt; |
| 2130 | 2138 | const zcu = pt.zcu; |
| 2131 | 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 | 2141 | const operand_ref = ty_op.operand; |
| 2134 | 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 | 2153 | // %x = block({ |
| 2139 | 2154 | // %1 = bit_cast(@res_ty, %y) |
| ... | ... | @@ -2148,54 +2163,32 @@ fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2148 | 2163 | |
| 2149 | 2164 | var inst_buf: [6]Air.Inst.Index = undefined; |
| 2150 | 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 |
| 2159 | | !dest_ty.isNonexhaustiveEnum(zcu) and |
| 2160 | | zcu.backendSupportsFeature(.is_named_enum_value)) |
| 2161 | | { |
| 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 | | } }, |
| 2169 | const cast_inst = block.addBitCast(l, dest_ty, operand_ref); |
| 2170 | const is_named_inst = block.add(l, .{ |
| 2171 | .tag = .is_named_enum_value, |
| 2172 | .data = .{ .un_op = cast_inst }, |
| 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 | 2185 | return .{ .ty_pl = .{ |
| 2193 | 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 | 2192 | const pt = l.pt; |
| 2200 | 2193 | const zcu = pt.zcu; |
| 2201 | 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 | 2207 | .@"enum" => true, |
| 2215 | 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 | 2214 | const operand_info = operand_scalar_ty.intInfo(zcu); |
| 2219 | 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 | 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 | 2232 | // The worst-case scenario in terms of total instructions and total condbrs is the case where |
| 2233 | 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 | 2324 | } }, |
| 2325 | 2325 | }); |
| 2326 | 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 | 2328 | assert(!is_vector); // vectors of enums don't exist |
| 2329 | 2329 | // We are building this: |
| 2330 | 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 | 2346 | cur_block = &condbr.then_block; |
| 2347 | 2347 | } |
| 2348 | 2348 | // Finally, just `br` to our outer `block`. |
| 2349 | | _ = cur_block.add(l, .{ |
| 2350 | | .tag = .br, |
| 2351 | | .data = .{ .br = .{ |
| 2352 | | .block_inst = orig_inst, |
| 2353 | | .operand = cast_inst.toRef(), |
| 2354 | | } }, |
| 2355 | | }); |
| 2349 | cur_block.addBr(l, orig_inst, cast_inst.toRef()); |
| 2350 | |
| 2356 | 2351 | // We might not have used all of the instructions; that's intentional. |
| 2357 | 2352 | _ = cur_block.stealRemainingCapacity(); |
| 2358 | 2353 | |
| 2354 | assert(condbr_idx != 0); // should have already returned `null` |
| 2359 | 2355 | for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l); |
| 2360 | 2356 | return .{ .ty_pl = .{ |
| 2361 | 2357 | .ty = Air.internedToRef(dest_ty.toIntern()), |