| ... | @@ -112,6 +112,402 @@ pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) | ... | @@ -112,6 +112,402 @@ pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) |
| 112 | l.tomb_bits[usize_index] &= ~mask; | 112 | l.tomb_bits[usize_index] &= ~mask; |
| 113 | } | 113 | } |
| 114 | | 114 | |
| | 115 | const OperandCategory = enum { |
| | 116 | /// The operand lives on, but this instruction cannot possibly mutate memory. |
| | 117 | none, |
| | 118 | /// The operand lives on and this instruction can mutate memory. |
| | 119 | write, |
| | 120 | /// The operand dies at this instruction. |
| | 121 | tomb, |
| | 122 | /// The operand lives on, and this instruction is noreturn. |
| | 123 | noret, |
| | 124 | /// This instruction is too complicated for analysis, no information is available. |
| | 125 | complex, |
| | 126 | }; |
| | 127 | |
| | 128 | /// Given an instruction that we are examining, and an operand that we are looking for, |
| | 129 | /// returns a classification. |
| | 130 | pub fn categorizeOperand( |
| | 131 | l: Liveness, |
| | 132 | air: Air, |
| | 133 | inst: Air.Inst.Index, |
| | 134 | operand: Air.Inst.Index, |
| | 135 | ) OperandCategory { |
| | 136 | const air_tags = air.instructions.items(.tag); |
| | 137 | const air_datas = air.instructions.items(.data); |
| | 138 | const operand_ref = Air.indexToRef(operand); |
| | 139 | switch (air_tags[inst]) { |
| | 140 | .add, |
| | 141 | .addwrap, |
| | 142 | .add_sat, |
| | 143 | .sub, |
| | 144 | .subwrap, |
| | 145 | .sub_sat, |
| | 146 | .mul, |
| | 147 | .mulwrap, |
| | 148 | .mul_sat, |
| | 149 | .div_float, |
| | 150 | .div_trunc, |
| | 151 | .div_floor, |
| | 152 | .div_exact, |
| | 153 | .rem, |
| | 154 | .mod, |
| | 155 | .bit_and, |
| | 156 | .bit_or, |
| | 157 | .xor, |
| | 158 | .cmp_lt, |
| | 159 | .cmp_lte, |
| | 160 | .cmp_eq, |
| | 161 | .cmp_gte, |
| | 162 | .cmp_gt, |
| | 163 | .cmp_neq, |
| | 164 | .bool_and, |
| | 165 | .bool_or, |
| | 166 | .array_elem_val, |
| | 167 | .slice_elem_val, |
| | 168 | .ptr_elem_val, |
| | 169 | .shl, |
| | 170 | .shl_exact, |
| | 171 | .shl_sat, |
| | 172 | .shr, |
| | 173 | .shr_exact, |
| | 174 | .min, |
| | 175 | .max, |
| | 176 | => { |
| | 177 | const o = air_datas[inst].bin_op; |
| | 178 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 179 | if (o.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 180 | return .none; |
| | 181 | }, |
| | 182 | |
| | 183 | .store, |
| | 184 | .atomic_store_unordered, |
| | 185 | .atomic_store_monotonic, |
| | 186 | .atomic_store_release, |
| | 187 | .atomic_store_seq_cst, |
| | 188 | .set_union_tag, |
| | 189 | => { |
| | 190 | const o = air_datas[inst].bin_op; |
| | 191 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 192 | if (o.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); |
| | 193 | return .write; |
| | 194 | }, |
| | 195 | |
| | 196 | .arg, |
| | 197 | .alloc, |
| | 198 | .ret_ptr, |
| | 199 | .constant, |
| | 200 | .const_ty, |
| | 201 | .breakpoint, |
| | 202 | .dbg_stmt, |
| | 203 | .dbg_inline_begin, |
| | 204 | .dbg_inline_end, |
| | 205 | .dbg_block_begin, |
| | 206 | .dbg_block_end, |
| | 207 | .unreach, |
| | 208 | .ret_addr, |
| | 209 | .frame_addr, |
| | 210 | .wasm_memory_size, |
| | 211 | .err_return_trace, |
| | 212 | => return .none, |
| | 213 | |
| | 214 | .fence => return .write, |
| | 215 | |
| | 216 | .not, |
| | 217 | .bitcast, |
| | 218 | .load, |
| | 219 | .fpext, |
| | 220 | .fptrunc, |
| | 221 | .intcast, |
| | 222 | .trunc, |
| | 223 | .optional_payload, |
| | 224 | .optional_payload_ptr, |
| | 225 | .wrap_optional, |
| | 226 | .unwrap_errunion_payload, |
| | 227 | .unwrap_errunion_err, |
| | 228 | .unwrap_errunion_payload_ptr, |
| | 229 | .unwrap_errunion_err_ptr, |
| | 230 | .wrap_errunion_payload, |
| | 231 | .wrap_errunion_err, |
| | 232 | .slice_ptr, |
| | 233 | .slice_len, |
| | 234 | .ptr_slice_len_ptr, |
| | 235 | .ptr_slice_ptr_ptr, |
| | 236 | .struct_field_ptr_index_0, |
| | 237 | .struct_field_ptr_index_1, |
| | 238 | .struct_field_ptr_index_2, |
| | 239 | .struct_field_ptr_index_3, |
| | 240 | .array_to_slice, |
| | 241 | .float_to_int, |
| | 242 | .int_to_float, |
| | 243 | .get_union_tag, |
| | 244 | .clz, |
| | 245 | .ctz, |
| | 246 | .popcount, |
| | 247 | .byte_swap, |
| | 248 | .bit_reverse, |
| | 249 | .splat, |
| | 250 | => { |
| | 251 | const o = air_datas[inst].ty_op; |
| | 252 | if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 253 | return .none; |
| | 254 | }, |
| | 255 | |
| | 256 | .optional_payload_ptr_set, |
| | 257 | .errunion_payload_ptr_set, |
| | 258 | => { |
| | 259 | const o = air_datas[inst].ty_op; |
| | 260 | if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 261 | return .write; |
| | 262 | }, |
| | 263 | |
| | 264 | .is_null, |
| | 265 | .is_non_null, |
| | 266 | .is_null_ptr, |
| | 267 | .is_non_null_ptr, |
| | 268 | .is_err, |
| | 269 | .is_non_err, |
| | 270 | .is_err_ptr, |
| | 271 | .is_non_err_ptr, |
| | 272 | .ptrtoint, |
| | 273 | .bool_to_int, |
| | 274 | .tag_name, |
| | 275 | .error_name, |
| | 276 | .sqrt, |
| | 277 | .sin, |
| | 278 | .cos, |
| | 279 | .tan, |
| | 280 | .exp, |
| | 281 | .exp2, |
| | 282 | .log, |
| | 283 | .log2, |
| | 284 | .log10, |
| | 285 | .fabs, |
| | 286 | .floor, |
| | 287 | .ceil, |
| | 288 | .round, |
| | 289 | .trunc_float, |
| | 290 | .cmp_lt_errors_len, |
| | 291 | => { |
| | 292 | const o = air_datas[inst].un_op; |
| | 293 | if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 294 | return .none; |
| | 295 | }, |
| | 296 | |
| | 297 | .ret, |
| | 298 | .ret_load, |
| | 299 | => { |
| | 300 | const o = air_datas[inst].un_op; |
| | 301 | if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .noret); |
| | 302 | return .noret; |
| | 303 | }, |
| | 304 | |
| | 305 | .set_err_return_trace => { |
| | 306 | const o = air_datas[inst].un_op; |
| | 307 | if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 308 | return .write; |
| | 309 | }, |
| | 310 | |
| | 311 | .add_with_overflow, |
| | 312 | .sub_with_overflow, |
| | 313 | .mul_with_overflow, |
| | 314 | .shl_with_overflow, |
| | 315 | .ptr_add, |
| | 316 | .ptr_sub, |
| | 317 | .ptr_elem_ptr, |
| | 318 | .slice_elem_ptr, |
| | 319 | .slice, |
| | 320 | => { |
| | 321 | const ty_pl = air_datas[inst].ty_pl; |
| | 322 | const extra = air.extraData(Air.Bin, ty_pl.payload).data; |
| | 323 | if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 324 | if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 325 | return .none; |
| | 326 | }, |
| | 327 | |
| | 328 | .dbg_var_ptr, |
| | 329 | .dbg_var_val, |
| | 330 | => { |
| | 331 | const o = air_datas[inst].pl_op.operand; |
| | 332 | if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 333 | return .none; |
| | 334 | }, |
| | 335 | |
| | 336 | .prefetch => { |
| | 337 | const prefetch = air_datas[inst].prefetch; |
| | 338 | if (prefetch.ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 339 | return .none; |
| | 340 | }, |
| | 341 | |
| | 342 | .call, .call_always_tail, .call_never_tail, .call_never_inline => { |
| | 343 | const inst_data = air_datas[inst].pl_op; |
| | 344 | const callee = inst_data.operand; |
| | 345 | const extra = air.extraData(Air.Call, inst_data.payload); |
| | 346 | const args = @ptrCast([]const Air.Inst.Ref, air.extra[extra.end..][0..extra.data.args_len]); |
| | 347 | if (args.len + 1 <= bpi - 1) { |
| | 348 | if (callee == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 349 | for (args) |arg, i| { |
| | 350 | if (arg == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(OperandInt, i + 1), .write); |
| | 351 | } |
| | 352 | return .write; |
| | 353 | } |
| | 354 | var bt = l.iterateBigTomb(inst); |
| | 355 | if (bt.feed()) { |
| | 356 | if (callee == operand_ref) return .tomb; |
| | 357 | } else { |
| | 358 | if (callee == operand_ref) return .write; |
| | 359 | } |
| | 360 | for (args) |arg| { |
| | 361 | if (bt.feed()) { |
| | 362 | if (arg == operand_ref) return .tomb; |
| | 363 | } else { |
| | 364 | if (arg == operand_ref) return .write; |
| | 365 | } |
| | 366 | } |
| | 367 | return .write; |
| | 368 | }, |
| | 369 | .select => { |
| | 370 | const pl_op = air_datas[inst].pl_op; |
| | 371 | const extra = air.extraData(Air.Bin, pl_op.payload).data; |
| | 372 | if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 373 | if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 374 | if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 2, .none); |
| | 375 | return .none; |
| | 376 | }, |
| | 377 | .shuffle => { |
| | 378 | const extra = air.extraData(Air.Shuffle, air_datas[inst].ty_pl.payload).data; |
| | 379 | if (extra.a == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 380 | if (extra.b == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 381 | return .none; |
| | 382 | }, |
| | 383 | .reduce => { |
| | 384 | const reduce = air_datas[inst].reduce; |
| | 385 | if (reduce.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 386 | return .none; |
| | 387 | }, |
| | 388 | .cmp_vector => { |
| | 389 | const extra = air.extraData(Air.VectorCmp, air_datas[inst].ty_pl.payload).data; |
| | 390 | if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 391 | if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 392 | return .none; |
| | 393 | }, |
| | 394 | .aggregate_init => { |
| | 395 | const ty_pl = air_datas[inst].ty_pl; |
| | 396 | const aggregate_ty = air.getRefType(ty_pl.ty); |
| | 397 | const len = @intCast(usize, aggregate_ty.arrayLen()); |
| | 398 | const elements = @ptrCast([]const Air.Inst.Ref, air.extra[ty_pl.payload..][0..len]); |
| | 399 | |
| | 400 | if (elements.len <= bpi - 1) { |
| | 401 | for (elements) |elem, i| { |
| | 402 | if (elem == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(OperandInt, i), .none); |
| | 403 | } |
| | 404 | return .none; |
| | 405 | } |
| | 406 | |
| | 407 | var bt = l.iterateBigTomb(inst); |
| | 408 | for (elements) |elem| { |
| | 409 | if (bt.feed()) { |
| | 410 | if (elem == operand_ref) return .tomb; |
| | 411 | } else { |
| | 412 | if (elem == operand_ref) return .write; |
| | 413 | } |
| | 414 | } |
| | 415 | return .write; |
| | 416 | }, |
| | 417 | .union_init => { |
| | 418 | const extra = air.extraData(Air.UnionInit, air_datas[inst].ty_pl.payload).data; |
| | 419 | if (extra.init == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 420 | return .none; |
| | 421 | }, |
| | 422 | .struct_field_ptr, .struct_field_val => { |
| | 423 | const extra = air.extraData(Air.StructField, air_datas[inst].ty_pl.payload).data; |
| | 424 | if (extra.struct_operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 425 | return .none; |
| | 426 | }, |
| | 427 | .field_parent_ptr => { |
| | 428 | const extra = air.extraData(Air.FieldParentPtr, air_datas[inst].ty_pl.payload).data; |
| | 429 | if (extra.field_ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 430 | return .none; |
| | 431 | }, |
| | 432 | .cmpxchg_strong, .cmpxchg_weak => { |
| | 433 | const extra = air.extraData(Air.Cmpxchg, air_datas[inst].ty_pl.payload).data; |
| | 434 | if (extra.ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 435 | if (extra.expected_value == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); |
| | 436 | if (extra.new_value == operand_ref) return matchOperandSmallIndex(l, inst, 2, .write); |
| | 437 | return .write; |
| | 438 | }, |
| | 439 | .mul_add => { |
| | 440 | const pl_op = air_datas[inst].pl_op; |
| | 441 | const extra = air.extraData(Air.Bin, pl_op.payload).data; |
| | 442 | if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 443 | if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); |
| | 444 | if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 2, .none); |
| | 445 | return .none; |
| | 446 | }, |
| | 447 | .atomic_load => { |
| | 448 | const ptr = air_datas[inst].atomic_load.ptr; |
| | 449 | if (ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 450 | return .none; |
| | 451 | }, |
| | 452 | .atomic_rmw => { |
| | 453 | const pl_op = air_datas[inst].pl_op; |
| | 454 | const extra = air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| | 455 | if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 456 | if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); |
| | 457 | return .write; |
| | 458 | }, |
| | 459 | .memset, |
| | 460 | .memcpy, |
| | 461 | => { |
| | 462 | const pl_op = air_datas[inst].pl_op; |
| | 463 | const extra = air.extraData(Air.Bin, pl_op.payload).data; |
| | 464 | if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| | 465 | if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); |
| | 466 | if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 2, .write); |
| | 467 | return .write; |
| | 468 | }, |
| | 469 | |
| | 470 | .br => { |
| | 471 | const br = air_datas[inst].br; |
| | 472 | if (br.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .noret); |
| | 473 | return .noret; |
| | 474 | }, |
| | 475 | .assembly => { |
| | 476 | return .complex; |
| | 477 | }, |
| | 478 | .block => { |
| | 479 | return .complex; |
| | 480 | }, |
| | 481 | .loop => { |
| | 482 | return .complex; |
| | 483 | }, |
| | 484 | .cond_br => { |
| | 485 | return .complex; |
| | 486 | }, |
| | 487 | .switch_br => { |
| | 488 | return .complex; |
| | 489 | }, |
| | 490 | .wasm_memory_grow => { |
| | 491 | const pl_op = air_datas[inst].pl_op; |
| | 492 | if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| | 493 | return .none; |
| | 494 | }, |
| | 495 | } |
| | 496 | } |
| | 497 | |
| | 498 | fn matchOperandSmallIndex( |
| | 499 | l: Liveness, |
| | 500 | inst: Air.Inst.Index, |
| | 501 | operand: OperandInt, |
| | 502 | default: OperandCategory, |
| | 503 | ) OperandCategory { |
| | 504 | if (operandDies(l, inst, operand)) { |
| | 505 | return .tomb; |
| | 506 | } else { |
| | 507 | return default; |
| | 508 | } |
| | 509 | } |
| | 510 | |
| 115 | /// Higher level API. | 511 | /// Higher level API. |
| 116 | pub const CondBrSlices = struct { | 512 | pub const CondBrSlices = struct { |
| 117 | then_deaths: []const Air.Inst.Index, | 513 | then_deaths: []const Air.Inst.Index, |