| ... | @@ -115,6 +115,8 @@ pub const Feature = enum { | ... | @@ -115,6 +115,8 @@ pub const Feature = enum { |
| 115 | scalarize_int_from_float_safe, | 115 | scalarize_int_from_float_safe, |
| 116 | scalarize_int_from_float_optimized_safe, | 116 | scalarize_int_from_float_optimized_safe, |
| 117 | scalarize_float_from_int, | 117 | scalarize_float_from_int, |
| | 118 | scalarize_reduce, |
| | 119 | scalarize_reduce_optimized, |
| 118 | scalarize_shuffle_one, | 120 | scalarize_shuffle_one, |
| 119 | scalarize_shuffle_two, | 121 | scalarize_shuffle_two, |
| 120 | scalarize_select, | 122 | scalarize_select, |
| ... | @@ -159,6 +161,27 @@ pub const Feature = enum { | ... | @@ -159,6 +161,27 @@ pub const Feature = enum { |
| 159 | /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bitcast`, `intcast`, and `bit_or`. | 161 | /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bitcast`, `intcast`, and `bit_or`. |
| 160 | expand_packed_aggregate_init, | 162 | expand_packed_aggregate_init, |
| 161 | | 163 | |
| | 164 | /// Replace all arithmetic operations on 16-bit floating-point types with calls to soft-float |
| | 165 | /// routines in compiler_rt, including `fptrunc`/`fpext`/`float_from_int`/`int_from_float` |
| | 166 | /// where the operand or target type is a 16-bit floating-point type. This feature implies: |
| | 167 | /// |
| | 168 | /// * scalarization of 16-bit float vector operations |
| | 169 | /// * expansion of safety-checked 16-bit float operations |
| | 170 | /// |
| | 171 | /// If this feature is enabled, the following AIR instruction tags may be emitted: |
| | 172 | /// * `.legalize_vec_elem_val` |
| | 173 | /// * `.legalize_vec_store_elem` |
| | 174 | /// * `.legalize_compiler_rt_call` |
| | 175 | soft_f16, |
| | 176 | /// Like `soft_f16`, but for 32-bit floating-point types. |
| | 177 | soft_f32, |
| | 178 | /// Like `soft_f16`, but for 64-bit floating-point types. |
| | 179 | soft_f64, |
| | 180 | /// Like `soft_f16`, but for 80-bit floating-point types. |
| | 181 | soft_f80, |
| | 182 | /// Like `soft_f16`, but for 128-bit floating-point types. |
| | 183 | soft_f128, |
| | 184 | |
| 162 | fn scalarize(tag: Air.Inst.Tag) Feature { | 185 | fn scalarize(tag: Air.Inst.Tag) Feature { |
| 163 | return switch (tag) { | 186 | return switch (tag) { |
| 164 | else => unreachable, | 187 | else => unreachable, |
| ... | @@ -238,6 +261,8 @@ pub const Feature = enum { | ... | @@ -238,6 +261,8 @@ pub const Feature = enum { |
| 238 | .int_from_float_safe => .scalarize_int_from_float_safe, | 261 | .int_from_float_safe => .scalarize_int_from_float_safe, |
| 239 | .int_from_float_optimized_safe => .scalarize_int_from_float_optimized_safe, | 262 | .int_from_float_optimized_safe => .scalarize_int_from_float_optimized_safe, |
| 240 | .float_from_int => .scalarize_float_from_int, | 263 | .float_from_int => .scalarize_float_from_int, |
| | 264 | .reduce => .scalarize_reduce, |
| | 265 | .reduce_optimized => .scalarize_reduce_optimized, |
| 241 | .shuffle_one => .scalarize_shuffle_one, | 266 | .shuffle_one => .scalarize_shuffle_one, |
| 242 | .shuffle_two => .scalarize_shuffle_two, | 267 | .shuffle_two => .scalarize_shuffle_two, |
| 243 | .select => .scalarize_select, | 268 | .select => .scalarize_select, |
| ... | @@ -283,6 +308,10 @@ fn extraData(l: *const Legalize, comptime T: type, index: usize) @TypeOf(Air.ext | ... | @@ -283,6 +308,10 @@ fn extraData(l: *const Legalize, comptime T: type, index: usize) @TypeOf(Air.ext |
| 283 | } | 308 | } |
| 284 | | 309 | |
| 285 | fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | 310 | fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| | 311 | // In zig1, this function needs a lot of eval branch quota, because all of the inlined feature |
| | 312 | // checks are comptime-evaluated (to ensure unused features are not included in the binary). |
| | 313 | @setEvalBranchQuota(4000); |
| | 314 | |
| 286 | const zcu = l.pt.zcu; | 315 | const zcu = l.pt.zcu; |
| 287 | const ip = &zcu.intern_pool; | 316 | const ip = &zcu.intern_pool; |
| 288 | for (0..body_len) |body_index| { | 317 | for (0..body_len) |body_index| { |
| ... | @@ -291,30 +320,67 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -291,30 +320,67 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 291 | .arg => {}, | 320 | .arg => {}, |
| 292 | inline .add, | 321 | inline .add, |
| 293 | .add_optimized, | 322 | .add_optimized, |
| 294 | .add_wrap, | | |
| 295 | .add_sat, | | |
| 296 | .sub, | 323 | .sub, |
| 297 | .sub_optimized, | 324 | .sub_optimized, |
| 298 | .sub_wrap, | | |
| 299 | .sub_sat, | | |
| 300 | .mul, | 325 | .mul, |
| 301 | .mul_optimized, | 326 | .mul_optimized, |
| 302 | .mul_wrap, | | |
| 303 | .mul_sat, | | |
| 304 | .div_float, | 327 | .div_float, |
| 305 | .div_float_optimized, | 328 | .div_float_optimized, |
| 306 | .div_trunc, | | |
| 307 | .div_trunc_optimized, | | |
| 308 | .div_floor, | | |
| 309 | .div_floor_optimized, | | |
| 310 | .div_exact, | 329 | .div_exact, |
| 311 | .div_exact_optimized, | 330 | .div_exact_optimized, |
| 312 | .rem, | 331 | .rem, |
| 313 | .rem_optimized, | 332 | .rem_optimized, |
| 314 | .mod, | | |
| 315 | .mod_optimized, | | |
| 316 | .max, | | |
| 317 | .min, | 333 | .min, |
| | 334 | .max, |
| | 335 | => |air_tag| { |
| | 336 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| | 337 | const ty = l.typeOf(bin_op.lhs); |
| | 338 | switch (l.wantScalarizeOrSoftFloat(air_tag, ty)) { |
| | 339 | .none => {}, |
| | 340 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), |
| | 341 | .soft_float => continue :inst try l.compilerRtCall( |
| | 342 | inst, |
| | 343 | softFloatFunc(air_tag, ty, zcu), |
| | 344 | &.{ bin_op.lhs, bin_op.rhs }, |
| | 345 | l.typeOf(bin_op.lhs), |
| | 346 | ), |
| | 347 | } |
| | 348 | }, |
| | 349 | inline .div_trunc, |
| | 350 | .div_trunc_optimized, |
| | 351 | .div_floor, |
| | 352 | .div_floor_optimized, |
| | 353 | => |air_tag| { |
| | 354 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| | 355 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { |
| | 356 | .none => {}, |
| | 357 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), |
| | 358 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorBlockPayload( |
| | 359 | inst, |
| | 360 | bin_op.lhs, |
| | 361 | bin_op.rhs, |
| | 362 | air_tag, |
| | 363 | )), |
| | 364 | } |
| | 365 | }, |
| | 366 | inline .mod, .mod_optimized => |air_tag| { |
| | 367 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| | 368 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { |
| | 369 | .none => {}, |
| | 370 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), |
| | 371 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatModBlockPayload( |
| | 372 | inst, |
| | 373 | bin_op.lhs, |
| | 374 | bin_op.rhs, |
| | 375 | )), |
| | 376 | } |
| | 377 | }, |
| | 378 | inline .add_wrap, |
| | 379 | .add_sat, |
| | 380 | .sub_wrap, |
| | 381 | .sub_sat, |
| | 382 | .mul_wrap, |
| | 383 | .mul_sat, |
| 318 | .bit_and, | 384 | .bit_and, |
| 319 | .bit_or, | 385 | .bit_or, |
| 320 | .xor, | 386 | .xor, |
| ... | @@ -408,20 +474,80 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -408,20 +474,80 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 408 | .popcount, | 474 | .popcount, |
| 409 | .byte_swap, | 475 | .byte_swap, |
| 410 | .bit_reverse, | 476 | .bit_reverse, |
| 411 | .abs, | | |
| 412 | .fptrunc, | | |
| 413 | .fpext, | | |
| 414 | .intcast, | 477 | .intcast, |
| 415 | .trunc, | 478 | .trunc, |
| 416 | .int_from_float, | | |
| 417 | .int_from_float_optimized, | | |
| 418 | .float_from_int, | | |
| 419 | => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { | 479 | => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { |
| 420 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | 480 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 421 | if (ty_op.ty.toType().isVector(zcu)) { | 481 | if (ty_op.ty.toType().isVector(zcu)) { |
| 422 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); | 482 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); |
| 423 | } | 483 | } |
| 424 | }, | 484 | }, |
| | 485 | .abs => { |
| | 486 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 487 | switch (l.wantScalarizeOrSoftFloat(.abs, ty_op.ty.toType())) { |
| | 488 | .none => {}, |
| | 489 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)), |
| | 490 | .soft_float => continue :inst try l.compilerRtCall( |
| | 491 | inst, |
| | 492 | softFloatFunc(.abs, ty_op.ty.toType(), zcu), |
| | 493 | &.{ty_op.operand}, |
| | 494 | ty_op.ty.toType(), |
| | 495 | ), |
| | 496 | } |
| | 497 | }, |
| | 498 | .fptrunc => { |
| | 499 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 500 | const src_ty = l.typeOf(ty_op.operand); |
| | 501 | const dest_ty = ty_op.ty.toType(); |
| | 502 | if (src_ty.zigTypeTag(zcu) == .vector) { |
| | 503 | if (l.features.has(.scalarize_fptrunc) or |
| | 504 | l.wantSoftFloatScalar(src_ty.childType(zcu)) or |
| | 505 | l.wantSoftFloatScalar(dest_ty.childType(zcu))) |
| | 506 | { |
| | 507 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); |
| | 508 | } |
| | 509 | } else if (l.wantSoftFloatScalar(src_ty) or l.wantSoftFloatScalar(dest_ty)) { |
| | 510 | continue :inst try l.compilerRtCall(inst, l.softFptruncFunc(src_ty, dest_ty), &.{ty_op.operand}, dest_ty); |
| | 511 | } |
| | 512 | }, |
| | 513 | .fpext => { |
| | 514 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 515 | const src_ty = l.typeOf(ty_op.operand); |
| | 516 | const dest_ty = ty_op.ty.toType(); |
| | 517 | if (src_ty.zigTypeTag(zcu) == .vector) { |
| | 518 | if (l.features.has(.scalarize_fpext) or |
| | 519 | l.wantSoftFloatScalar(src_ty.childType(zcu)) or |
| | 520 | l.wantSoftFloatScalar(dest_ty.childType(zcu))) |
| | 521 | { |
| | 522 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); |
| | 523 | } |
| | 524 | } else if (l.wantSoftFloatScalar(src_ty) or l.wantSoftFloatScalar(dest_ty)) { |
| | 525 | continue :inst try l.compilerRtCall(inst, l.softFpextFunc(src_ty, dest_ty), &.{ty_op.operand}, dest_ty); |
| | 526 | } |
| | 527 | }, |
| | 528 | inline .int_from_float, .int_from_float_optimized => |air_tag| { |
| | 529 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 530 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(ty_op.operand))) { |
| | 531 | .none => {}, |
| | 532 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)), |
| | 533 | .soft_float => switch (try l.softIntFromFloat(inst)) { |
| | 534 | .call => |func| continue :inst try l.compilerRtCall(inst, func, &.{ty_op.operand}, ty_op.ty.toType()), |
| | 535 | .block_payload => |data| continue :inst l.replaceInst(inst, .block, data), |
| | 536 | }, |
| | 537 | } |
| | 538 | }, |
| | 539 | .float_from_int => { |
| | 540 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| | 541 | const dest_ty = ty_op.ty.toType(); |
| | 542 | switch (l.wantScalarizeOrSoftFloat(.float_from_int, dest_ty)) { |
| | 543 | .none => {}, |
| | 544 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)), |
| | 545 | .soft_float => switch (try l.softFloatFromInt(inst)) { |
| | 546 | .call => |func| continue :inst try l.compilerRtCall(inst, func, &.{ty_op.operand}, dest_ty), |
| | 547 | .block_payload => |data| continue :inst l.replaceInst(inst, .block, data), |
| | 548 | }, |
| | 549 | } |
| | 550 | }, |
| 425 | .bitcast => if (l.features.has(.scalarize_bitcast)) { | 551 | .bitcast => if (l.features.has(.scalarize_bitcast)) { |
| 426 | if (try l.scalarizeBitcastBlockPayload(inst)) |payload| { | 552 | if (try l.scalarizeBitcastBlockPayload(inst)) |payload| { |
| 427 | continue :inst l.replaceInst(inst, .block, payload); | 553 | continue :inst l.replaceInst(inst, .block, payload); |
| ... | @@ -436,22 +562,25 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -436,22 +562,25 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 436 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); | 562 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); |
| 437 | } | 563 | } |
| 438 | }, | 564 | }, |
| 439 | .int_from_float_safe => if (l.features.has(.expand_int_from_float_safe)) { | 565 | inline .int_from_float_safe, |
| 440 | assert(!l.features.has(.scalarize_int_from_float_safe)); | 566 | .int_from_float_optimized_safe, |
| 441 | continue :inst l.replaceInst(inst, .block, try l.safeIntFromFloatBlockPayload(inst, false)); | 567 | => |air_tag| { |
| 442 | } else if (l.features.has(.scalarize_int_from_float_safe)) { | 568 | const optimized = air_tag == .int_from_float_optimized_safe; |
| 443 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | 569 | const expand_feature = switch (air_tag) { |
| 444 | if (ty_op.ty.toType().isVector(zcu)) { | 570 | .int_from_float_safe => .expand_int_from_float_safe, |
| 445 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); | 571 | .int_from_float_optimized_safe => .expand_int_from_float_optimized_safe, |
| | 572 | else => unreachable, |
| | 573 | }; |
| | 574 | if (l.features.has(expand_feature)) { |
| | 575 | assert(!l.features.has(.scalarize(air_tag))); |
| | 576 | continue :inst l.replaceInst(inst, .block, try l.safeIntFromFloatBlockPayload(inst, optimized)); |
| 446 | } | 577 | } |
| 447 | }, | | |
| 448 | .int_from_float_optimized_safe => if (l.features.has(.expand_int_from_float_optimized_safe)) { | | |
| 449 | assert(!l.features.has(.scalarize_int_from_float_optimized_safe)); | | |
| 450 | continue :inst l.replaceInst(inst, .block, try l.safeIntFromFloatBlockPayload(inst, true)); | | |
| 451 | } else if (l.features.has(.scalarize_int_from_float_optimized_safe)) { | | |
| 452 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | 578 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 453 | if (ty_op.ty.toType().isVector(zcu)) { | 579 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(ty_op.operand))) { |
| 454 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); | 580 | .none => {}, |
| | 581 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)), |
| | 582 | // Expand the safety check so that soft-float can rewrite the unchecked operation. |
| | 583 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.safeIntFromFloatBlockPayload(inst, optimized)), |
| 455 | } | 584 | } |
| 456 | }, | 585 | }, |
| 457 | .block, .loop => { | 586 | .block, .loop => { |
| ... | @@ -483,12 +612,26 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -483,12 +612,26 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 483 | .ceil, | 612 | .ceil, |
| 484 | .round, | 613 | .round, |
| 485 | .trunc_float, | 614 | .trunc_float, |
| 486 | .neg, | 615 | => |air_tag| { |
| 487 | .neg_optimized, | 616 | const operand = l.air_instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 488 | => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { | 617 | const ty = l.typeOf(operand); |
| 489 | const un_op = l.air_instructions.items(.data)[@intFromEnum(inst)].un_op; | 618 | switch (l.wantScalarizeOrSoftFloat(air_tag, ty)) { |
| 490 | if (l.typeOf(un_op).isVector(zcu)) { | 619 | .none => {}, |
| 491 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .un_op)); | 620 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .un_op)), |
| | 621 | .soft_float => continue :inst try l.compilerRtCall( |
| | 622 | inst, |
| | 623 | softFloatFunc(air_tag, ty, zcu), |
| | 624 | &.{operand}, |
| | 625 | l.typeOf(operand), |
| | 626 | ), |
| | 627 | } |
| | 628 | }, |
| | 629 | inline .neg, .neg_optimized => |air_tag| { |
| | 630 | const operand = l.air_instructions.items(.data)[@intFromEnum(inst)].un_op; |
| | 631 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(operand))) { |
| | 632 | .none => {}, |
| | 633 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .un_op)), |
| | 634 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatNegBlockPayload(inst, operand)), |
| 492 | } | 635 | } |
| 493 | }, | 636 | }, |
| 494 | .cmp_lt, | 637 | .cmp_lt, |
| ... | @@ -503,11 +646,24 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -503,11 +646,24 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 503 | .cmp_gt_optimized, | 646 | .cmp_gt_optimized, |
| 504 | .cmp_neq, | 647 | .cmp_neq, |
| 505 | .cmp_neq_optimized, | 648 | .cmp_neq_optimized, |
| 506 | => {}, | 649 | => |air_tag| { |
| 507 | inline .cmp_vector, .cmp_vector_optimized => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { | 650 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| | 651 | const ty = l.typeOf(bin_op.lhs); |
| | 652 | if (l.wantSoftFloatScalar(ty)) { |
| | 653 | continue :inst l.replaceInst( |
| | 654 | inst, |
| | 655 | .block, |
| | 656 | try l.softFloatCmpBlockPayload(inst, ty, air_tag.toCmpOp().?, bin_op.lhs, bin_op.rhs), |
| | 657 | ); |
| | 658 | } |
| | 659 | }, |
| | 660 | inline .cmp_vector, .cmp_vector_optimized => |air_tag| { |
| 508 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 661 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 509 | if (ty_pl.ty.toType().isVector(zcu)) { | 662 | const payload = l.extraData(Air.VectorCmp, ty_pl.payload).data; |
| 510 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .cmp_vector)); | 663 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(payload.lhs))) { |
| | 664 | .none => {}, |
| | 665 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .cmp_vector)), |
| | 666 | .soft_float => unreachable, // the operand is not a scalar |
| 511 | } | 667 | } |
| 512 | }, | 668 | }, |
| 513 | .cond_br => { | 669 | .cond_br => { |
| ... | @@ -615,16 +771,27 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -615,16 +771,27 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 615 | .ptr_elem_ptr, | 771 | .ptr_elem_ptr, |
| 616 | .array_to_slice, | 772 | .array_to_slice, |
| 617 | => {}, | 773 | => {}, |
| 618 | .reduce, .reduce_optimized => if (l.features.has(.reduce_one_elem_to_bitcast)) { | 774 | inline .reduce, .reduce_optimized => |air_tag| { |
| 619 | const reduce = l.air_instructions.items(.data)[@intFromEnum(inst)].reduce; | 775 | const reduce = l.air_instructions.items(.data)[@intFromEnum(inst)].reduce; |
| 620 | const vector_ty = l.typeOf(reduce.operand); | 776 | const vector_ty = l.typeOf(reduce.operand); |
| 621 | switch (vector_ty.vectorLen(zcu)) { | 777 | if (l.features.has(.reduce_one_elem_to_bitcast)) { |
| 622 | 0 => unreachable, | 778 | switch (vector_ty.vectorLen(zcu)) { |
| 623 | 1 => continue :inst l.replaceInst(inst, .bitcast, .{ .ty_op = .{ | 779 | 0 => unreachable, |
| 624 | .ty = .fromType(vector_ty.childType(zcu)), | 780 | 1 => continue :inst l.replaceInst(inst, .bitcast, .{ .ty_op = .{ |
| 625 | .operand = reduce.operand, | 781 | .ty = .fromType(vector_ty.childType(zcu)), |
| 626 | } }), | 782 | .operand = reduce.operand, |
| 627 | else => {}, | 783 | } }), |
| | 784 | else => {}, |
| | 785 | } |
| | 786 | } |
| | 787 | switch (l.wantScalarizeOrSoftFloat(air_tag, vector_ty)) { |
| | 788 | .none => {}, |
| | 789 | .scalarize => continue :inst l.replaceInst( |
| | 790 | inst, |
| | 791 | .block, |
| | 792 | try l.scalarizeReduceBlockPayload(inst, air_tag == .reduce_optimized), |
| | 793 | ), |
| | 794 | .soft_float => unreachable, // the operand is not a scalar |
| 628 | } | 795 | } |
| 629 | }, | 796 | }, |
| 630 | .splat => if (l.features.has(.splat_one_elem_to_bitcast)) { | 797 | .splat => if (l.features.has(.splat_one_elem_to_bitcast)) { |
| ... | @@ -638,14 +805,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -638,14 +805,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 638 | else => {}, | 805 | else => {}, |
| 639 | } | 806 | } |
| 640 | }, | 807 | }, |
| 641 | .shuffle_one => if (l.features.has(.scalarize_shuffle_one)) { | 808 | .shuffle_one => { |
| 642 | continue :inst l.replaceInst(inst, .block, try l.scalarizeShuffleOneBlockPayload(inst)); | 809 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| | 810 | switch (l.wantScalarizeOrSoftFloat(.shuffle_one, ty_pl.ty.toType())) { |
| | 811 | .none => {}, |
| | 812 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeShuffleOneBlockPayload(inst)), |
| | 813 | .soft_float => unreachable, // the operand is not a scalar |
| | 814 | } |
| 643 | }, | 815 | }, |
| 644 | .shuffle_two => if (l.features.has(.scalarize_shuffle_two)) { | 816 | .shuffle_two => { |
| 645 | continue :inst l.replaceInst(inst, .block, try l.scalarizeShuffleTwoBlockPayload(inst)); | 817 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| | 818 | switch (l.wantScalarizeOrSoftFloat(.shuffle_two, ty_pl.ty.toType())) { |
| | 819 | .none => {}, |
| | 820 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeShuffleTwoBlockPayload(inst)), |
| | 821 | .soft_float => unreachable, // the operand is not a scalar |
| | 822 | } |
| 646 | }, | 823 | }, |
| 647 | .select => if (l.features.has(.scalarize_select)) { | 824 | .select => { |
| 648 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .select)); | 825 | const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| | 826 | const bin = l.extraData(Air.Bin, pl_op.payload).data; |
| | 827 | switch (l.wantScalarizeOrSoftFloat(.select, l.typeOf(bin.lhs))) { |
| | 828 | .none => {}, |
| | 829 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .select)), |
| | 830 | .soft_float => unreachable, // the operand is not a scalar |
| | 831 | } |
| 649 | }, | 832 | }, |
| 650 | .memset, | 833 | .memset, |
| 651 | .memset_safe, | 834 | .memset_safe, |
| ... | @@ -685,10 +868,17 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -685,10 +868,17 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 685 | } | 868 | } |
| 686 | }, | 869 | }, |
| 687 | .union_init, .prefetch => {}, | 870 | .union_init, .prefetch => {}, |
| 688 | .mul_add => if (l.features.has(.scalarize_mul_add)) { | 871 | .mul_add => { |
| 689 | const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op; | 872 | const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 690 | if (l.typeOf(pl_op.operand).isVector(zcu)) { | 873 | const ty = l.typeOf(pl_op.operand); |
| 691 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .pl_op_bin)); | 874 | switch (l.wantScalarizeOrSoftFloat(.mul_add, ty)) { |
| | 875 | .none => {}, |
| | 876 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .pl_op_bin)), |
| | 877 | .soft_float => { |
| | 878 | const bin = l.extraData(Air.Bin, pl_op.payload).data; |
| | 879 | const func = softFloatFunc(.mul_add, ty, zcu); |
| | 880 | continue :inst try l.compilerRtCall(inst, func, &.{ bin.lhs, bin.rhs, pl_op.operand }, ty); |
| | 881 | }, |
| 692 | } | 882 | } |
| 693 | }, | 883 | }, |
| 694 | .field_parent_ptr, | 884 | .field_parent_ptr, |
| ... | @@ -709,6 +899,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -709,6 +899,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 709 | .work_group_id, | 899 | .work_group_id, |
| 710 | .legalize_vec_elem_val, | 900 | .legalize_vec_elem_val, |
| 711 | .legalize_vec_store_elem, | 901 | .legalize_vec_store_elem, |
| | 902 | .legalize_compiler_rt_call, |
| 712 | => {}, | 903 | => {}, |
| 713 | } | 904 | } |
| 714 | } | 905 | } |
| ... | @@ -1606,6 +1797,128 @@ fn scalarizeOverflowBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error! | ... | @@ -1606,6 +1797,128 @@ fn scalarizeOverflowBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error! |
| 1606 | .payload = try l.addBlockBody(main_block.body()), | 1797 | .payload = try l.addBlockBody(main_block.body()), |
| 1607 | } }; | 1798 | } }; |
| 1608 | } | 1799 | } |
| | 1800 | fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimized: bool) Error!Air.Inst.Data { |
| | 1801 | const pt = l.pt; |
| | 1802 | const zcu = pt.zcu; |
| | 1803 | |
| | 1804 | const reduce = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].reduce; |
| | 1805 | |
| | 1806 | const vector_ty = l.typeOf(reduce.operand); |
| | 1807 | const scalar_ty = vector_ty.childType(zcu); |
| | 1808 | |
| | 1809 | const ident_val: Value = switch (reduce.operation) { |
| | 1810 | // identity for add is 0; identity for OR and XOR is all 0 bits |
| | 1811 | .Or, .Xor, .Add => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1812 | .int => try pt.intValue(scalar_ty, 0), |
| | 1813 | .float => try pt.floatValue(scalar_ty, 0.0), |
| | 1814 | else => unreachable, |
| | 1815 | }, |
| | 1816 | // identity for multiplication is 1 |
| | 1817 | .Mul => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1818 | .int => try pt.intValue(scalar_ty, 1), |
| | 1819 | .float => try pt.floatValue(scalar_ty, 1.0), |
| | 1820 | else => unreachable, |
| | 1821 | }, |
| | 1822 | // identity for AND is all 1 bits |
| | 1823 | .And => switch (scalar_ty.intInfo(zcu).signedness) { |
| | 1824 | .unsigned => try scalar_ty.maxIntScalar(pt, scalar_ty), |
| | 1825 | .signed => try pt.intValue(scalar_ty, -1), |
| | 1826 | }, |
| | 1827 | // identity for @min is maximum value |
| | 1828 | .Min => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1829 | .int => try scalar_ty.maxIntScalar(pt, scalar_ty), |
| | 1830 | .float => try pt.floatValue(scalar_ty, std.math.inf(f32)), |
| | 1831 | else => unreachable, |
| | 1832 | }, |
| | 1833 | // identity for @max is minimum value |
| | 1834 | .Max => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1835 | .int => try scalar_ty.minIntScalar(pt, scalar_ty), |
| | 1836 | .float => try pt.floatValue(scalar_ty, -std.math.inf(f32)), |
| | 1837 | else => unreachable, |
| | 1838 | }, |
| | 1839 | }; |
| | 1840 | |
| | 1841 | const op_tag: Air.Inst.Tag = switch (reduce.operation) { |
| | 1842 | .Or => .bit_or, |
| | 1843 | .And => .bit_and, |
| | 1844 | .Xor => .xor, |
| | 1845 | .Min => .min, |
| | 1846 | .Max => .max, |
| | 1847 | .Add => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1848 | .int => .add_wrap, |
| | 1849 | .float => if (optimized) .add_optimized else .add, |
| | 1850 | else => unreachable, |
| | 1851 | }, |
| | 1852 | .Mul => switch (scalar_ty.zigTypeTag(zcu)) { |
| | 1853 | .int => .mul_wrap, |
| | 1854 | .float => if (optimized) .mul_optimized else .mul, |
| | 1855 | else => unreachable, |
| | 1856 | }, |
| | 1857 | }; |
| | 1858 | |
| | 1859 | // %1 = block(Scalar, { |
| | 1860 | // %2 = alloc(*usize) |
| | 1861 | // %3 = alloc(*Scalar) |
| | 1862 | // %4 = store(%2, @zero_usize) |
| | 1863 | // %5 = store(%3, <Scalar, 0>) // or whatever the identity is for this operator |
| | 1864 | // %6 = loop({ |
| | 1865 | // %7 = load(%2) |
| | 1866 | // %8 = legalize_vec_elem_val(orig_operand, %7) |
| | 1867 | // %9 = load(%3) |
| | 1868 | // %10 = add(%8, %9) // or whatever the operator is |
| | 1869 | // %11 = cmp_eq(%7, <usize, N-1>) |
| | 1870 | // %12 = cond_br(%11, { |
| | 1871 | // %13 = br(%1, %10) |
| | 1872 | // }, { |
| | 1873 | // %14 = store(%3, %10) |
| | 1874 | // %15 = add(%7, @one_usize) |
| | 1875 | // %16 = store(%2, %15) |
| | 1876 | // %17 = repeat(%6) |
| | 1877 | // }) |
| | 1878 | // }) |
| | 1879 | // }) |
| | 1880 | |
| | 1881 | var inst_buf: [16]Air.Inst.Index = undefined; |
| | 1882 | var main_block: Block = .init(&inst_buf); |
| | 1883 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| | 1884 | |
| | 1885 | const index_ptr = main_block.addTy(l, .alloc, .ptr_usize).toRef(); |
| | 1886 | const accum_ptr = main_block.addTy(l, .alloc, try pt.singleMutPtrType(scalar_ty)).toRef(); |
| | 1887 | _ = main_block.addBinOp(l, .store, index_ptr, .zero_usize); |
| | 1888 | _ = main_block.addBinOp(l, .store, accum_ptr, .fromValue(ident_val)); |
| | 1889 | |
| | 1890 | var loop: Loop = .init(l, &main_block); |
| | 1891 | loop.block = .init(main_block.stealRemainingCapacity()); |
| | 1892 | |
| | 1893 | const index_val = loop.block.addTyOp(l, .load, .usize, index_ptr).toRef(); |
| | 1894 | const elem_val = loop.block.addBinOp(l, .legalize_vec_elem_val, reduce.operand, index_val).toRef(); |
| | 1895 | const old_accum = loop.block.addTyOp(l, .load, scalar_ty, accum_ptr).toRef(); |
| | 1896 | const new_accum = loop.block.addBinOp(l, op_tag, old_accum, elem_val).toRef(); |
| | 1897 | |
| | 1898 | const is_end_val = loop.block.addBinOp(l, .cmp_eq, index_val, .fromValue(try pt.intValue(.usize, vector_ty.vectorLen(zcu) - 1))).toRef(); |
| | 1899 | |
| | 1900 | var condbr: CondBr = .init(l, is_end_val, &loop.block, .{}); |
| | 1901 | |
| | 1902 | condbr.then_block = .init(loop.block.stealRemainingCapacity()); |
| | 1903 | condbr.then_block.addBr(l, orig_inst, new_accum); |
| | 1904 | |
| | 1905 | condbr.else_block = .init(condbr.then_block.stealRemainingCapacity()); |
| | 1906 | _ = condbr.else_block.addBinOp(l, .store, accum_ptr, new_accum); |
| | 1907 | const new_index_val = condbr.else_block.addBinOp(l, .add, index_val, .one_usize).toRef(); |
| | 1908 | _ = condbr.else_block.addBinOp(l, .store, index_ptr, new_index_val); |
| | 1909 | _ = condbr.else_block.add(l, .{ |
| | 1910 | .tag = .repeat, |
| | 1911 | .data = .{ .repeat = .{ .loop_inst = loop.inst } }, |
| | 1912 | }); |
| | 1913 | |
| | 1914 | try condbr.finish(l); |
| | 1915 | try loop.finish(l); |
| | 1916 | |
| | 1917 | return .{ .ty_pl = .{ |
| | 1918 | .ty = .fromType(scalar_ty), |
| | 1919 | .payload = try l.addBlockBody(main_block.body()), |
| | 1920 | } }; |
| | 1921 | } |
| 1609 | | 1922 | |
| 1610 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { | 1923 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { |
| 1611 | const pt = l.pt; | 1924 | const pt = l.pt; |
| ... | @@ -2298,6 +2611,22 @@ const Block = struct { | ... | @@ -2298,6 +2611,22 @@ const Block = struct { |
| 2298 | }); | 2611 | }); |
| 2299 | } | 2612 | } |
| 2300 | | 2613 | |
| | 2614 | fn addCompilerRtCall(b: *Block, l: *Legalize, func: Air.CompilerRtFunc, args: []const Air.Inst.Ref) Error!Air.Inst.Index { |
| | 2615 | return b.add(l, .{ |
| | 2616 | .tag = .legalize_compiler_rt_call, |
| | 2617 | .data = .{ .legalize_compiler_rt_call = .{ |
| | 2618 | .func = func, |
| | 2619 | .payload = payload: { |
| | 2620 | const extra_len = @typeInfo(Air.Call).@"struct".fields.len + args.len; |
| | 2621 | try l.air_extra.ensureUnusedCapacity(l.pt.zcu.gpa, extra_len); |
| | 2622 | const index = l.addExtra(Air.Call, .{ .args_len = @intCast(args.len) }) catch unreachable; |
| | 2623 | l.air_extra.appendSliceAssumeCapacity(@ptrCast(args)); |
| | 2624 | break :payload index; |
| | 2625 | }, |
| | 2626 | } }, |
| | 2627 | }); |
| | 2628 | } |
| | 2629 | |
| 2301 | /// Adds the code to call the panic handler `panic_id`. This is usually `.call` then `.unreach`, | 2630 | /// Adds the code to call the panic handler `panic_id`. This is usually `.call` then `.unreach`, |
| 2302 | /// but if `Zcu.Feature.panic_fn` is unsupported, we lower to `.trap` instead. | 2631 | /// but if `Zcu.Feature.panic_fn` is unsupported, we lower to `.trap` instead. |
| 2303 | fn addPanic(b: *Block, l: *Legalize, panic_id: Zcu.SimplePanicId) Error!void { | 2632 | fn addPanic(b: *Block, l: *Legalize, panic_id: Zcu.SimplePanicId) Error!void { |
| ... | @@ -2365,14 +2694,7 @@ const Block = struct { | ... | @@ -2365,14 +2694,7 @@ const Block = struct { |
| 2365 | optimized: bool, | 2694 | optimized: bool, |
| 2366 | ) Air.Inst.Index { | 2695 | ) Air.Inst.Index { |
| 2367 | return b.add(l, .{ | 2696 | return b.add(l, .{ |
| 2368 | .tag = switch (op) { | 2697 | .tag = .fromCmpOp(op, optimized), |
| 2369 | .lt => if (optimized) .cmp_lt_optimized else .cmp_lt, | | |
| 2370 | .lte => if (optimized) .cmp_lte_optimized else .cmp_lte, | | |
| 2371 | .eq => if (optimized) .cmp_eq_optimized else .cmp_eq, | | |
| 2372 | .gte => if (optimized) .cmp_gte_optimized else .cmp_gte, | | |
| 2373 | .gt => if (optimized) .cmp_gt_optimized else .cmp_gt, | | |
| 2374 | .neq => if (optimized) .cmp_neq_optimized else .cmp_neq, | | |
| 2375 | }, | | |
| 2376 | .data = .{ .bin_op = .{ | 2698 | .data = .{ .bin_op = .{ |
| 2377 | .lhs = lhs, | 2699 | .lhs = lhs, |
| 2378 | .rhs = rhs, | 2700 | .rhs = rhs, |
| ... | @@ -2399,6 +2721,82 @@ const Block = struct { | ... | @@ -2399,6 +2721,82 @@ const Block = struct { |
| 2399 | return operand; | 2721 | return operand; |
| 2400 | } | 2722 | } |
| 2401 | | 2723 | |
| | 2724 | /// This function emits *two* instructions. |
| | 2725 | fn addSoftFloatCmp( |
| | 2726 | b: *Block, |
| | 2727 | l: *Legalize, |
| | 2728 | float_ty: Type, |
| | 2729 | op: std.math.CompareOperator, |
| | 2730 | lhs: Air.Inst.Ref, |
| | 2731 | rhs: Air.Inst.Ref, |
| | 2732 | ) Error!Air.Inst.Ref { |
| | 2733 | const pt = l.pt; |
| | 2734 | const target = pt.zcu.getTarget(); |
| | 2735 | const use_aeabi = target.cpu.arch.isArm() and switch (target.abi) { |
| | 2736 | .eabi, |
| | 2737 | .eabihf, |
| | 2738 | .musleabi, |
| | 2739 | .musleabihf, |
| | 2740 | .gnueabi, |
| | 2741 | .gnueabihf, |
| | 2742 | .android, |
| | 2743 | .androideabi, |
| | 2744 | => true, |
| | 2745 | else => false, |
| | 2746 | }; |
| | 2747 | const func: Air.CompilerRtFunc, const ret_cmp_op: std.math.CompareOperator = switch (float_ty.floatBits(target)) { |
| | 2748 | // zig fmt: off |
| | 2749 | 16 => switch (op) { |
| | 2750 | .eq => .{ .__eqhf2, .eq }, |
| | 2751 | .neq => .{ .__nehf2, .neq }, |
| | 2752 | .lt => .{ .__lthf2, .lt }, |
| | 2753 | .lte => .{ .__lehf2, .lte }, |
| | 2754 | .gt => .{ .__gthf2, .gt }, |
| | 2755 | .gte => .{ .__gehf2, .gte }, |
| | 2756 | }, |
| | 2757 | 32 => switch (op) { |
| | 2758 | .eq => if (use_aeabi) .{ .__aeabi_fcmpeq, .neq } else .{ .__eqsf2, .eq }, |
| | 2759 | .neq => if (use_aeabi) .{ .__aeabi_fcmpeq, .eq } else .{ .__nesf2, .neq }, |
| | 2760 | .lt => if (use_aeabi) .{ .__aeabi_fcmplt, .neq } else .{ .__ltsf2, .lt }, |
| | 2761 | .lte => if (use_aeabi) .{ .__aeabi_fcmple, .neq } else .{ .__lesf2, .lte }, |
| | 2762 | .gt => if (use_aeabi) .{ .__aeabi_fcmpgt, .neq } else .{ .__gtsf2, .gt }, |
| | 2763 | .gte => if (use_aeabi) .{ .__aeabi_fcmpge, .neq } else .{ .__gesf2, .gte }, |
| | 2764 | }, |
| | 2765 | 64 => switch (op) { |
| | 2766 | .eq => if (use_aeabi) .{ .__aeabi_dcmpeq, .neq } else .{ .__eqdf2, .eq }, |
| | 2767 | .neq => if (use_aeabi) .{ .__aeabi_dcmpeq, .eq } else .{ .__nedf2, .neq }, |
| | 2768 | .lt => if (use_aeabi) .{ .__aeabi_dcmplt, .neq } else .{ .__ltdf2, .lt }, |
| | 2769 | .lte => if (use_aeabi) .{ .__aeabi_dcmple, .neq } else .{ .__ledf2, .lte }, |
| | 2770 | .gt => if (use_aeabi) .{ .__aeabi_dcmpgt, .neq } else .{ .__gtdf2, .gt }, |
| | 2771 | .gte => if (use_aeabi) .{ .__aeabi_dcmpge, .neq } else .{ .__gedf2, .gte }, |
| | 2772 | }, |
| | 2773 | 80 => switch (op) { |
| | 2774 | .eq => .{ .__eqxf2, .eq }, |
| | 2775 | .neq => .{ .__nexf2, .neq }, |
| | 2776 | .lt => .{ .__ltxf2, .lt }, |
| | 2777 | .lte => .{ .__lexf2, .lte }, |
| | 2778 | .gt => .{ .__gtxf2, .gt }, |
| | 2779 | .gte => .{ .__gexf2, .gte }, |
| | 2780 | }, |
| | 2781 | 128 => switch (op) { |
| | 2782 | .eq => .{ .__eqtf2, .eq }, |
| | 2783 | .neq => .{ .__netf2, .neq }, |
| | 2784 | .lt => .{ .__lttf2, .lt }, |
| | 2785 | .lte => .{ .__letf2, .lte }, |
| | 2786 | .gt => .{ .__gttf2, .gt }, |
| | 2787 | .gte => .{ .__getf2, .gte }, |
| | 2788 | }, |
| | 2789 | else => unreachable, |
| | 2790 | // zig fmt: on |
| | 2791 | }; |
| | 2792 | const call_inst = try b.addCompilerRtCall(l, func, &.{ lhs, rhs }); |
| | 2793 | const raw_result = call_inst.toRef(); |
| | 2794 | assert(l.typeOf(raw_result).toIntern() == .i32_type); |
| | 2795 | const zero_i32: Air.Inst.Ref = .fromValue(try pt.intValue(.i32, 0)); |
| | 2796 | const ret_cmp_tag: Air.Inst.Tag = .fromCmpOp(ret_cmp_op, false); |
| | 2797 | return b.addBinOp(l, ret_cmp_tag, raw_result, zero_i32).toRef(); |
| | 2798 | } |
| | 2799 | |
| 2402 | /// Returns the unused capacity of `b.instructions`, and shrinks `b.instructions` down to `b.len`. | 2800 | /// Returns the unused capacity of `b.instructions`, and shrinks `b.instructions` down to `b.len`. |
| 2403 | /// This is useful when you've provided a buffer big enough for all your instructions, but you are | 2801 | /// This is useful when you've provided a buffer big enough for all your instructions, but you are |
| 2404 | /// now starting a new block and some of them need to live there instead. | 2802 | /// now starting a new block and some of them need to live there instead. |
| ... | @@ -2525,6 +2923,484 @@ inline fn replaceInst(l: *Legalize, inst: Air.Inst.Index, comptime tag: Air.Inst | ... | @@ -2525,6 +2923,484 @@ inline fn replaceInst(l: *Legalize, inst: Air.Inst.Index, comptime tag: Air.Inst |
| 2525 | return tag; | 2923 | return tag; |
| 2526 | } | 2924 | } |
| 2527 | | 2925 | |
| | 2926 | fn compilerRtCall( |
| | 2927 | l: *Legalize, |
| | 2928 | orig_inst: Air.Inst.Index, |
| | 2929 | func: Air.CompilerRtFunc, |
| | 2930 | args: []const Air.Inst.Ref, |
| | 2931 | result_ty: Type, |
| | 2932 | ) Error!Air.Inst.Tag { |
| | 2933 | const zcu = l.pt.zcu; |
| | 2934 | const gpa = zcu.gpa; |
| | 2935 | |
| | 2936 | const func_ret_ty = func.returnType(); |
| | 2937 | |
| | 2938 | if (func_ret_ty.toIntern() == result_ty.toIntern()) { |
| | 2939 | try l.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + args.len); |
| | 2940 | const payload = l.addExtra(Air.Call, .{ .args_len = @intCast(args.len) }) catch unreachable; |
| | 2941 | l.air_extra.appendSliceAssumeCapacity(@ptrCast(args)); |
| | 2942 | return l.replaceInst(orig_inst, .legalize_compiler_rt_call, .{ .legalize_compiler_rt_call = .{ |
| | 2943 | .func = func, |
| | 2944 | .payload = payload, |
| | 2945 | } }); |
| | 2946 | } |
| | 2947 | |
| | 2948 | // We need to bitcast the result to an "alias" type (e.g. c_int/i32, c_longdouble/f128). |
| | 2949 | |
| | 2950 | assert(func_ret_ty.bitSize(zcu) == result_ty.bitSize(zcu)); |
| | 2951 | |
| | 2952 | var inst_buf: [3]Air.Inst.Index = undefined; |
| | 2953 | var main_block: Block = .init(&inst_buf); |
| | 2954 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); |
| | 2955 | |
| | 2956 | const call_inst = try main_block.addCompilerRtCall(l, func, args); |
| | 2957 | const casted_result = main_block.addBitCast(l, result_ty, call_inst.toRef()); |
| | 2958 | main_block.addBr(l, orig_inst, casted_result); |
| | 2959 | |
| | 2960 | return l.replaceInst(orig_inst, .block, .{ .ty_pl = .{ |
| | 2961 | .ty = .fromType(result_ty), |
| | 2962 | .payload = try l.addBlockBody(main_block.body()), |
| | 2963 | } }); |
| | 2964 | } |
| | 2965 | |
| | 2966 | fn softFptruncFunc(l: *const Legalize, src_ty: Type, dst_ty: Type) Air.CompilerRtFunc { |
| | 2967 | const target = l.pt.zcu.getTarget(); |
| | 2968 | const src_bits = src_ty.floatBits(target); |
| | 2969 | const dst_bits = dst_ty.floatBits(target); |
| | 2970 | assert(dst_bits < src_bits); |
| | 2971 | const to_f16_func: Air.CompilerRtFunc = switch (src_bits) { |
| | 2972 | 128 => .__trunctfhf2, |
| | 2973 | 80 => .__truncxfhf2, |
| | 2974 | 64 => .__truncdfhf2, |
| | 2975 | 32 => .__truncsfhf2, |
| | 2976 | else => unreachable, |
| | 2977 | }; |
| | 2978 | const offset: u8 = switch (dst_bits) { |
| | 2979 | 16 => 0, |
| | 2980 | 32 => 1, |
| | 2981 | 64 => 2, |
| | 2982 | 80 => 3, |
| | 2983 | else => unreachable, |
| | 2984 | }; |
| | 2985 | return @enumFromInt(@intFromEnum(to_f16_func) + offset); |
| | 2986 | } |
| | 2987 | fn softFpextFunc(l: *const Legalize, src_ty: Type, dst_ty: Type) Air.CompilerRtFunc { |
| | 2988 | const target = l.pt.zcu.getTarget(); |
| | 2989 | const src_bits = src_ty.floatBits(target); |
| | 2990 | const dst_bits = dst_ty.floatBits(target); |
| | 2991 | assert(dst_bits > src_bits); |
| | 2992 | const to_f128_func: Air.CompilerRtFunc = switch (src_bits) { |
| | 2993 | 16 => .__extendhftf2, |
| | 2994 | 32 => .__extendsftf2, |
| | 2995 | 64 => .__extenddftf2, |
| | 2996 | 80 => .__extendxftf2, |
| | 2997 | else => unreachable, |
| | 2998 | }; |
| | 2999 | const offset: u8 = switch (dst_bits) { |
| | 3000 | 128 => 0, |
| | 3001 | 80 => 1, |
| | 3002 | 64 => 2, |
| | 3003 | 32 => 3, |
| | 3004 | else => unreachable, |
| | 3005 | }; |
| | 3006 | return @enumFromInt(@intFromEnum(to_f128_func) + offset); |
| | 3007 | } |
| | 3008 | fn softFloatFromInt(l: *Legalize, orig_inst: Air.Inst.Index) Error!union(enum) { |
| | 3009 | call: Air.CompilerRtFunc, |
| | 3010 | block_payload: Air.Inst.Data, |
| | 3011 | } { |
| | 3012 | const pt = l.pt; |
| | 3013 | const zcu = pt.zcu; |
| | 3014 | const target = zcu.getTarget(); |
| | 3015 | |
| | 3016 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; |
| | 3017 | const dest_ty = ty_op.ty.toType(); |
| | 3018 | const src_ty = l.typeOf(ty_op.operand); |
| | 3019 | |
| | 3020 | const src_info = src_ty.intInfo(zcu); |
| | 3021 | const float_off: u32 = switch (dest_ty.floatBits(target)) { |
| | 3022 | 16 => 0, |
| | 3023 | 32 => 1, |
| | 3024 | 64 => 2, |
| | 3025 | 80 => 3, |
| | 3026 | 128 => 4, |
| | 3027 | else => unreachable, |
| | 3028 | }; |
| | 3029 | const base: Air.CompilerRtFunc = switch (src_info.signedness) { |
| | 3030 | .signed => .__floatsihf, |
| | 3031 | .unsigned => .__floatunsihf, |
| | 3032 | }; |
| | 3033 | fixed: { |
| | 3034 | const extended_int_bits: u16, const int_bits_off: u32 = switch (src_info.bits) { |
| | 3035 | 0...32 => .{ 32, 0 }, |
| | 3036 | 33...64 => .{ 64, 5 }, |
| | 3037 | 65...128 => .{ 128, 10 }, |
| | 3038 | else => break :fixed, |
| | 3039 | }; |
| | 3040 | // x86_64-windows uses an odd callconv for 128-bit integers, so we use the |
| | 3041 | // arbitrary-precision routine in that case for simplicity. |
| | 3042 | if (target.cpu.arch == .x86_64 and target.os.tag == .windows and extended_int_bits == 128) { |
| | 3043 | break :fixed; |
| | 3044 | } |
| | 3045 | |
| | 3046 | const func: Air.CompilerRtFunc = @enumFromInt(@intFromEnum(base) + int_bits_off + float_off); |
| | 3047 | if (extended_int_bits == src_info.bits) return .{ .call = func }; |
| | 3048 | |
| | 3049 | // We need to emit a block which first sign/zero-extends to the right type and *then* calls |
| | 3050 | // the required routine. |
| | 3051 | const extended_ty = try l.pt.intType(src_info.signedness, extended_int_bits); |
| | 3052 | |
| | 3053 | var inst_buf: [4]Air.Inst.Index = undefined; |
| | 3054 | var main_block: Block = .init(&inst_buf); |
| | 3055 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| | 3056 | |
| | 3057 | const extended_val = main_block.addTyOp(l, .intcast, extended_ty, ty_op.operand).toRef(); |
| | 3058 | const call_inst = try main_block.addCompilerRtCall(l, func, &.{extended_val}); |
| | 3059 | const casted_result = main_block.addBitCast(l, dest_ty, call_inst.toRef()); |
| | 3060 | main_block.addBr(l, orig_inst, casted_result); |
| | 3061 | |
| | 3062 | return .{ .block_payload = .{ .ty_pl = .{ |
| | 3063 | .ty = .fromType(dest_ty), |
| | 3064 | .payload = try l.addBlockBody(main_block.body()), |
| | 3065 | } } }; |
| | 3066 | } |
| | 3067 | |
| | 3068 | // We need to emit a block which puts the integer into an `alloc` (possibly sign/zero-extended) |
| | 3069 | // and calls an arbitrary-width conversion routine. |
| | 3070 | |
| | 3071 | const func: Air.CompilerRtFunc = @enumFromInt(@intFromEnum(base) + 15 + float_off); |
| | 3072 | |
| | 3073 | // The extended integer routines expect the integer representation where the integer is |
| | 3074 | // effectively zero- or sign-extended to its ABI size. We represent that by intcasting to |
| | 3075 | // such an integer type and passing a pointer to *that*. |
| | 3076 | const extended_ty = try pt.intType(src_info.signedness, @intCast(src_ty.abiSize(zcu) * 8)); |
| | 3077 | assert(extended_ty.abiSize(zcu) == src_ty.abiSize(zcu)); |
| | 3078 | |
| | 3079 | var inst_buf: [6]Air.Inst.Index = undefined; |
| | 3080 | var main_block: Block = .init(&inst_buf); |
| | 3081 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| | 3082 | |
| | 3083 | const extended_val: Air.Inst.Ref = if (extended_ty.toIntern() != src_ty.toIntern()) ext: { |
| | 3084 | break :ext main_block.addTyOp(l, .intcast, extended_ty, ty_op.operand).toRef(); |
| | 3085 | } else ext: { |
| | 3086 | _ = main_block.stealCapacity(1); |
| | 3087 | break :ext ty_op.operand; |
| | 3088 | }; |
| | 3089 | const extended_ptr = main_block.addTy(l, .alloc, try pt.singleMutPtrType(extended_ty)).toRef(); |
| | 3090 | _ = main_block.addBinOp(l, .store, extended_ptr, extended_val); |
| | 3091 | const bits_val = try pt.intValue(.usize, src_info.bits); |
| | 3092 | const call_inst = try main_block.addCompilerRtCall(l, func, &.{ extended_ptr, .fromValue(bits_val) }); |
| | 3093 | const casted_result = main_block.addBitCast(l, dest_ty, call_inst.toRef()); |
| | 3094 | main_block.addBr(l, orig_inst, casted_result); |
| | 3095 | |
| | 3096 | return .{ .block_payload = .{ .ty_pl = .{ |
| | 3097 | .ty = .fromType(dest_ty), |
| | 3098 | .payload = try l.addBlockBody(main_block.body()), |
| | 3099 | } } }; |
| | 3100 | } |
| | 3101 | fn softIntFromFloat(l: *Legalize, orig_inst: Air.Inst.Index) Error!union(enum) { |
| | 3102 | call: Air.CompilerRtFunc, |
| | 3103 | block_payload: Air.Inst.Data, |
| | 3104 | } { |
| | 3105 | const pt = l.pt; |
| | 3106 | const zcu = pt.zcu; |
| | 3107 | const target = zcu.getTarget(); |
| | 3108 | |
| | 3109 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; |
| | 3110 | const src_ty = l.typeOf(ty_op.operand); |
| | 3111 | const dest_ty = ty_op.ty.toType(); |
| | 3112 | |
| | 3113 | const dest_info = dest_ty.intInfo(zcu); |
| | 3114 | const float_off: u32 = switch (src_ty.floatBits(target)) { |
| | 3115 | 16 => 0, |
| | 3116 | 32 => 1, |
| | 3117 | 64 => 2, |
| | 3118 | 80 => 3, |
| | 3119 | 128 => 4, |
| | 3120 | else => unreachable, |
| | 3121 | }; |
| | 3122 | const base: Air.CompilerRtFunc = switch (dest_info.signedness) { |
| | 3123 | .signed => .__fixhfsi, |
| | 3124 | .unsigned => .__fixunshfsi, |
| | 3125 | }; |
| | 3126 | fixed: { |
| | 3127 | const extended_int_bits: u16, const int_bits_off: u32 = switch (dest_info.bits) { |
| | 3128 | 0...32 => .{ 32, 0 }, |
| | 3129 | 33...64 => .{ 64, 5 }, |
| | 3130 | 65...128 => .{ 128, 10 }, |
| | 3131 | else => break :fixed, |
| | 3132 | }; |
| | 3133 | // x86_64-windows uses an odd callconv for 128-bit integers, so we use the |
| | 3134 | // arbitrary-precision routine in that case for simplicity. |
| | 3135 | if (target.cpu.arch == .x86_64 and target.os.tag == .windows and extended_int_bits == 128) { |
| | 3136 | break :fixed; |
| | 3137 | } |
| | 3138 | |
| | 3139 | const func: Air.CompilerRtFunc = @enumFromInt(@intFromEnum(base) + int_bits_off + float_off); |
| | 3140 | if (extended_int_bits == dest_info.bits) return .{ .call = func }; |
| | 3141 | |
| | 3142 | // We need to emit a block which calls the routine and then casts to the required type. |
| | 3143 | |
| | 3144 | var inst_buf: [3]Air.Inst.Index = undefined; |
| | 3145 | var main_block: Block = .init(&inst_buf); |
| | 3146 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| | 3147 | |
| | 3148 | const call_inst = try main_block.addCompilerRtCall(l, func, &.{ty_op.operand}); |
| | 3149 | const casted_val = main_block.addTyOp(l, .intcast, dest_ty, call_inst.toRef()).toRef(); |
| | 3150 | main_block.addBr(l, orig_inst, casted_val); |
| | 3151 | |
| | 3152 | return .{ .block_payload = .{ .ty_pl = .{ |
| | 3153 | .ty = .fromType(dest_ty), |
| | 3154 | .payload = try l.addBlockBody(main_block.body()), |
| | 3155 | } } }; |
| | 3156 | } |
| | 3157 | |
| | 3158 | // We need to emit a block which calls an arbitrary-width conversion routine, then loads the |
| | 3159 | // integer from an `alloc` and possibly truncates it. |
| | 3160 | const func: Air.CompilerRtFunc = @enumFromInt(@intFromEnum(base) + 15 + float_off); |
| | 3161 | |
| | 3162 | const extended_ty = try pt.intType(dest_info.signedness, @intCast(dest_ty.abiSize(zcu) * 8)); |
| | 3163 | assert(extended_ty.abiSize(zcu) == dest_ty.abiSize(zcu)); |
| | 3164 | |
| | 3165 | var inst_buf: [5]Air.Inst.Index = undefined; |
| | 3166 | var main_block: Block = .init(&inst_buf); |
| | 3167 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); |
| | 3168 | |
| | 3169 | const extended_ptr = main_block.addTy(l, .alloc, try pt.singleMutPtrType(extended_ty)).toRef(); |
| | 3170 | const bits_val = try pt.intValue(.usize, dest_info.bits); |
| | 3171 | _ = try main_block.addCompilerRtCall(l, func, &.{ extended_ptr, .fromValue(bits_val), ty_op.operand }); |
| | 3172 | const extended_val = main_block.addTyOp(l, .load, extended_ty, extended_ptr).toRef(); |
| | 3173 | const result_val = main_block.addTyOp(l, .intcast, dest_ty, extended_val).toRef(); |
| | 3174 | main_block.addBr(l, orig_inst, result_val); |
| | 3175 | |
| | 3176 | return .{ .block_payload = .{ .ty_pl = .{ |
| | 3177 | .ty = .fromType(dest_ty), |
| | 3178 | .payload = try l.addBlockBody(main_block.body()), |
| | 3179 | } } }; |
| | 3180 | } |
| | 3181 | fn softFloatFunc(op: Air.Inst.Tag, float_ty: Type, zcu: *const Zcu) Air.CompilerRtFunc { |
| | 3182 | const f16_func: Air.CompilerRtFunc = switch (op) { |
| | 3183 | .add, .add_optimized => .__addhf3, |
| | 3184 | .sub, .sub_optimized => .__subhf3, |
| | 3185 | .mul, .mul_optimized => .__mulhf3, |
| | 3186 | |
| | 3187 | .div_float, |
| | 3188 | .div_float_optimized, |
| | 3189 | .div_exact, |
| | 3190 | .div_exact_optimized, |
| | 3191 | => .__divhf3, |
| | 3192 | |
| | 3193 | .min => .__fminh, |
| | 3194 | .max => .__fmaxh, |
| | 3195 | |
| | 3196 | .ceil => .__ceilh, |
| | 3197 | .floor => .__floorh, |
| | 3198 | .trunc_float => .__trunch, |
| | 3199 | .round => .__roundh, |
| | 3200 | |
| | 3201 | .log => .__logh, |
| | 3202 | .log2 => .__log2h, |
| | 3203 | .log10 => .__log10h, |
| | 3204 | |
| | 3205 | .exp => .__exph, |
| | 3206 | .exp2 => .__exp2h, |
| | 3207 | |
| | 3208 | .sin => .__sinh, |
| | 3209 | .cos => .__cosh, |
| | 3210 | .tan => .__tanh, |
| | 3211 | |
| | 3212 | .abs => .__fabsh, |
| | 3213 | .sqrt => .__sqrth, |
| | 3214 | .rem, .rem_optimized => .__fmodh, |
| | 3215 | .mul_add => .__fmah, |
| | 3216 | |
| | 3217 | else => unreachable, |
| | 3218 | }; |
| | 3219 | const offset: u8 = switch (float_ty.floatBits(zcu.getTarget())) { |
| | 3220 | 16 => 0, |
| | 3221 | 32 => 1, |
| | 3222 | 64 => 2, |
| | 3223 | 80 => 3, |
| | 3224 | 128 => 4, |
| | 3225 | else => unreachable, |
| | 3226 | }; |
| | 3227 | return @enumFromInt(@intFromEnum(f16_func) + offset); |
| | 3228 | } |
| | 3229 | |
| | 3230 | fn softFloatNegBlockPayload( |
| | 3231 | l: *Legalize, |
| | 3232 | orig_inst: Air.Inst.Index, |
| | 3233 | operand: Air.Inst.Ref, |
| | 3234 | ) Error!Air.Inst.Data { |
| | 3235 | const pt = l.pt; |
| | 3236 | const zcu = pt.zcu; |
| | 3237 | const gpa = zcu.gpa; |
| | 3238 | |
| | 3239 | const float_ty = l.typeOfIndex(orig_inst); |
| | 3240 | |
| | 3241 | const int_ty: Type, const sign_bit: Value = switch (float_ty.floatBits(zcu.getTarget())) { |
| | 3242 | 16 => .{ .u16, try pt.intValue(.u16, @as(u16, 1) << 15) }, |
| | 3243 | 32 => .{ .u32, try pt.intValue(.u32, @as(u32, 1) << 31) }, |
| | 3244 | 64 => .{ .u64, try pt.intValue(.u64, @as(u64, 1) << 63) }, |
| | 3245 | 80 => .{ .u80, try pt.intValue(.u80, @as(u80, 1) << 79) }, |
| | 3246 | 128 => .{ .u128, try pt.intValue(.u128, @as(u128, 1) << 127) }, |
| | 3247 | else => unreachable, |
| | 3248 | }; |
| | 3249 | |
| | 3250 | const sign_bit_ref: Air.Inst.Ref = .fromValue(sign_bit); |
| | 3251 | |
| | 3252 | var inst_buf: [4]Air.Inst.Index = undefined; |
| | 3253 | var main_block: Block = .init(&inst_buf); |
| | 3254 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); |
| | 3255 | |
| | 3256 | const operand_as_int = main_block.addBitCast(l, int_ty, operand); |
| | 3257 | const result_as_int = main_block.addBinOp(l, .xor, operand_as_int, sign_bit_ref).toRef(); |
| | 3258 | const result = main_block.addBitCast(l, float_ty, result_as_int); |
| | 3259 | main_block.addBr(l, orig_inst, result); |
| | 3260 | |
| | 3261 | return .{ .ty_pl = .{ |
| | 3262 | .ty = .fromType(float_ty), |
| | 3263 | .payload = try l.addBlockBody(main_block.body()), |
| | 3264 | } }; |
| | 3265 | } |
| | 3266 | |
| | 3267 | fn softFloatDivTruncFloorBlockPayload( |
| | 3268 | l: *Legalize, |
| | 3269 | orig_inst: Air.Inst.Index, |
| | 3270 | lhs: Air.Inst.Ref, |
| | 3271 | rhs: Air.Inst.Ref, |
| | 3272 | air_tag: Air.Inst.Tag, |
| | 3273 | ) Error!Air.Inst.Data { |
| | 3274 | const zcu = l.pt.zcu; |
| | 3275 | const gpa = zcu.gpa; |
| | 3276 | |
| | 3277 | const float_ty = l.typeOfIndex(orig_inst); |
| | 3278 | |
| | 3279 | const floor_tag: Air.Inst.Tag = switch (air_tag) { |
| | 3280 | .div_trunc, .div_trunc_optimized => .trunc_float, |
| | 3281 | .div_floor, .div_floor_optimized => .floor, |
| | 3282 | else => unreachable, |
| | 3283 | }; |
| | 3284 | |
| | 3285 | var inst_buf: [4]Air.Inst.Index = undefined; |
| | 3286 | var main_block: Block = .init(&inst_buf); |
| | 3287 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); |
| | 3288 | |
| | 3289 | const div_inst = try main_block.addCompilerRtCall(l, softFloatFunc(.div_float, float_ty, zcu), &.{ lhs, rhs }); |
| | 3290 | const floor_inst = try main_block.addCompilerRtCall(l, softFloatFunc(floor_tag, float_ty, zcu), &.{div_inst.toRef()}); |
| | 3291 | const casted_result = main_block.addBitCast(l, float_ty, floor_inst.toRef()); |
| | 3292 | main_block.addBr(l, orig_inst, casted_result); |
| | 3293 | |
| | 3294 | return .{ .ty_pl = .{ |
| | 3295 | .ty = .fromType(float_ty), |
| | 3296 | .payload = try l.addBlockBody(main_block.body()), |
| | 3297 | } }; |
| | 3298 | } |
| | 3299 | fn softFloatModBlockPayload( |
| | 3300 | l: *Legalize, |
| | 3301 | orig_inst: Air.Inst.Index, |
| | 3302 | lhs: Air.Inst.Ref, |
| | 3303 | rhs: Air.Inst.Ref, |
| | 3304 | ) Error!Air.Inst.Data { |
| | 3305 | const pt = l.pt; |
| | 3306 | const zcu = pt.zcu; |
| | 3307 | const gpa = zcu.gpa; |
| | 3308 | |
| | 3309 | const float_ty = l.typeOfIndex(orig_inst); |
| | 3310 | |
| | 3311 | var inst_buf: [10]Air.Inst.Index = undefined; |
| | 3312 | var main_block: Block = .init(&inst_buf); |
| | 3313 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); |
| | 3314 | |
| | 3315 | const rem = try main_block.addCompilerRtCall(l, softFloatFunc(.rem, float_ty, zcu), &.{ lhs, rhs }); |
| | 3316 | const lhs_lt_zero = try main_block.addSoftFloatCmp(l, float_ty, .lt, lhs, .fromValue(try pt.floatValue(float_ty, 0.0))); |
| | 3317 | |
| | 3318 | var condbr: CondBr = .init(l, lhs_lt_zero, &main_block, .{}); |
| | 3319 | condbr.then_block = .init(main_block.stealRemainingCapacity()); |
| | 3320 | { |
| | 3321 | const add = try condbr.then_block.addCompilerRtCall(l, softFloatFunc(.add, float_ty, zcu), &.{ rem.toRef(), rhs }); |
| | 3322 | const inner_rem = try condbr.then_block.addCompilerRtCall(l, softFloatFunc(.rem, float_ty, zcu), &.{ add.toRef(), rhs }); |
| | 3323 | const casted_result = condbr.then_block.addBitCast(l, float_ty, inner_rem.toRef()); |
| | 3324 | condbr.then_block.addBr(l, orig_inst, casted_result); |
| | 3325 | } |
| | 3326 | condbr.else_block = .init(condbr.then_block.stealRemainingCapacity()); |
| | 3327 | { |
| | 3328 | const casted_result = condbr.else_block.addBitCast(l, float_ty, rem.toRef()); |
| | 3329 | condbr.else_block.addBr(l, orig_inst, casted_result); |
| | 3330 | } |
| | 3331 | |
| | 3332 | try condbr.finish(l); |
| | 3333 | |
| | 3334 | return .{ .ty_pl = .{ |
| | 3335 | .ty = .fromType(float_ty), |
| | 3336 | .payload = try l.addBlockBody(main_block.body()), |
| | 3337 | } }; |
| | 3338 | } |
| | 3339 | fn softFloatCmpBlockPayload( |
| | 3340 | l: *Legalize, |
| | 3341 | orig_inst: Air.Inst.Index, |
| | 3342 | float_ty: Type, |
| | 3343 | op: std.math.CompareOperator, |
| | 3344 | lhs: Air.Inst.Ref, |
| | 3345 | rhs: Air.Inst.Ref, |
| | 3346 | ) Error!Air.Inst.Data { |
| | 3347 | const pt = l.pt; |
| | 3348 | const gpa = pt.zcu.gpa; |
| | 3349 | |
| | 3350 | var inst_buf: [3]Air.Inst.Index = undefined; |
| | 3351 | var main_block: Block = .init(&inst_buf); |
| | 3352 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); |
| | 3353 | |
| | 3354 | const result = try main_block.addSoftFloatCmp(l, float_ty, op, lhs, rhs); |
| | 3355 | main_block.addBr(l, orig_inst, result); |
| | 3356 | |
| | 3357 | return .{ .ty_pl = .{ |
| | 3358 | .ty = .bool_type, |
| | 3359 | .payload = try l.addBlockBody(main_block.body()), |
| | 3360 | } }; |
| | 3361 | } |
| | 3362 | |
| | 3363 | /// `inline` to propagate potentially comptime-known return value. |
| | 3364 | inline fn wantScalarizeOrSoftFloat( |
| | 3365 | l: *const Legalize, |
| | 3366 | comptime air_tag: Air.Inst.Tag, |
| | 3367 | ty: Type, |
| | 3368 | ) enum { |
| | 3369 | none, |
| | 3370 | scalarize, |
| | 3371 | soft_float, |
| | 3372 | } { |
| | 3373 | const zcu = l.pt.zcu; |
| | 3374 | const is_vec, const scalar_ty = switch (ty.zigTypeTag(zcu)) { |
| | 3375 | .vector => .{ true, ty.childType(zcu) }, |
| | 3376 | else => .{ false, ty }, |
| | 3377 | }; |
| | 3378 | |
| | 3379 | if (is_vec and l.features.has(.scalarize(air_tag))) return .scalarize; |
| | 3380 | |
| | 3381 | if (l.wantSoftFloatScalar(scalar_ty)) { |
| | 3382 | return if (is_vec) .scalarize else .soft_float; |
| | 3383 | } |
| | 3384 | return .none; |
| | 3385 | } |
| | 3386 | |
| | 3387 | /// `inline` to propagate potentially comptime-known return value. |
| | 3388 | inline fn wantSoftFloatScalar(l: *const Legalize, ty: Type) bool { |
| | 3389 | const zcu = l.pt.zcu; |
| | 3390 | return switch (ty.zigTypeTag(zcu)) { |
| | 3391 | .vector => unreachable, |
| | 3392 | .float => switch (ty.floatBits(zcu.getTarget())) { |
| | 3393 | 16 => l.features.has(.soft_f16), |
| | 3394 | 32 => l.features.has(.soft_f32), |
| | 3395 | 64 => l.features.has(.soft_f64), |
| | 3396 | 80 => l.features.has(.soft_f80), |
| | 3397 | 128 => l.features.has(.soft_f128), |
| | 3398 | else => unreachable, |
| | 3399 | }, |
| | 3400 | else => false, |
| | 3401 | }; |
| | 3402 | } |
| | 3403 | |
| 2528 | const Air = @import("../Air.zig"); | 3404 | const Air = @import("../Air.zig"); |
| 2529 | const assert = std.debug.assert; | 3405 | const assert = std.debug.assert; |
| 2530 | const dev = @import("../dev.zig"); | 3406 | const dev = @import("../dev.zig"); |