| author | |
| committer | |
| log | b9a95f2dd94e6175322d3388c3936eb600ec90ea |
| tree | 186a31fd920ca47bc15c3a426b9dfa8d67756ada |
| parent | 29f41896ed9d99e82a88f4b63efa182ca0d2f93c |
- modify AstGen binOpExt()/assignBinOpExt() to accept generic extended payload T
- rework Sema zirSatArithmetic() to use existing sema.analyzeArithmetic() by adding an `opt_extended` parameter.
- add airSatOp() to codegen/c.zig
- add saturating functions to src/link/C/zig.h5 files changed, 262 insertions(+), 44 deletions(-)
src/AstGen.zig+32-30| ... | ... | @@ -535,7 +535,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 535 | 535 | return rvalue(gz, rl, .void_value, node); |
| 536 | 536 | }, |
| 537 | 537 | .assign_bit_shift_left_sat => { |
| 538 | try assignBinOpExt(gz, scope, node, .shl_with_saturation); | |
| 538 | try assignBinOpExt(gz, scope, node, .shl_with_saturation, Zir.Inst.SaturatingArithmetic); | |
| 539 | 539 | return rvalue(gz, rl, .void_value, node); |
| 540 | 540 | }, |
| 541 | 541 | .assign_bit_shift_right => { |
| ... | ... | @@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 568 | 568 | return rvalue(gz, rl, .void_value, node); |
| 569 | 569 | }, |
| 570 | 570 | .assign_sub_sat => { |
| 571 | try assignBinOpExt(gz, scope, node, .sub_with_saturation); | |
| 571 | try assignBinOpExt(gz, scope, node, .sub_with_saturation, Zir.Inst.SaturatingArithmetic); | |
| 572 | 572 | return rvalue(gz, rl, .void_value, node); |
| 573 | 573 | }, |
| 574 | 574 | .assign_mod => { |
| ... | ... | @@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 584 | 584 | return rvalue(gz, rl, .void_value, node); |
| 585 | 585 | }, |
| 586 | 586 | .assign_add_sat => { |
| 587 | try assignBinOpExt(gz, scope, node, .add_with_saturation); | |
| 587 | try assignBinOpExt(gz, scope, node, .add_with_saturation, Zir.Inst.SaturatingArithmetic); | |
| 588 | 588 | return rvalue(gz, rl, .void_value, node); |
| 589 | 589 | }, |
| 590 | 590 | .assign_mul => { |
| ... | ... | @@ -596,24 +596,24 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 596 | 596 | return rvalue(gz, rl, .void_value, node); |
| 597 | 597 | }, |
| 598 | 598 | .assign_mul_sat => { |
| 599 | try assignBinOpExt(gz, scope, node, .mul_with_saturation); | |
| 599 | try assignBinOpExt(gz, scope, node, .mul_with_saturation, Zir.Inst.SaturatingArithmetic); | |
| 600 | 600 | return rvalue(gz, rl, .void_value, node); |
| 601 | 601 | }, |
| 602 | 602 | |
| 603 | 603 | // zig fmt: off |
| 604 | 604 | .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), |
| 605 | .bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation), | |
| 605 | .bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation, Zir.Inst.SaturatingArithmetic), | |
| 606 | 606 | .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), |
| 607 | 607 | |
| 608 | 608 | .add => return simpleBinOp(gz, scope, rl, node, .add), |
| 609 | 609 | .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap), |
| 610 | .add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation), | |
| 610 | .add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation, Zir.Inst.SaturatingArithmetic), | |
| 611 | 611 | .sub => return simpleBinOp(gz, scope, rl, node, .sub), |
| 612 | 612 | .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap), |
| 613 | .sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation), | |
| 613 | .sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation, Zir.Inst.SaturatingArithmetic), | |
| 614 | 614 | .mul => return simpleBinOp(gz, scope, rl, node, .mul), |
| 615 | 615 | .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap), |
| 616 | .mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation), | |
| 616 | .mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation, Zir.Inst.SaturatingArithmetic), | |
| 617 | 617 | .div => return simpleBinOp(gz, scope, rl, node, .div), |
| 618 | 618 | .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), |
| 619 | 619 | .bit_and => { |
| ... | ... | @@ -2713,6 +2713,28 @@ fn assignOp( |
| 2713 | 2713 | _ = try gz.addBin(.store, lhs_ptr, result); |
| 2714 | 2714 | } |
| 2715 | 2715 | |
| 2716 | // TODO: is there an existing way to do this? | |
| 2717 | // TODO: likely rename this to reflect result_loc == .none or add more params to make it more general | |
| 2718 | fn binOpExt( | |
| 2719 | gz: *GenZir, | |
| 2720 | scope: *Scope, | |
| 2721 | rl: ResultLoc, | |
| 2722 | infix_node: Ast.Node.Index, | |
| 2723 | lhs_node: Ast.Node.Index, | |
| 2724 | rhs_node: Ast.Node.Index, | |
| 2725 | tag: Zir.Inst.Extended, | |
| 2726 | comptime T: type, | |
| 2727 | ) InnerError!Zir.Inst.Ref { | |
| 2728 | const lhs = try expr(gz, scope, .none, lhs_node); | |
| 2729 | const rhs = try expr(gz, scope, .none, rhs_node); | |
| 2730 | const result = try gz.addExtendedPayload(tag, T{ | |
| 2731 | .node = gz.nodeIndexToRelative(infix_node), | |
| 2732 | .lhs = lhs, | |
| 2733 | .rhs = rhs, | |
| 2734 | }); | |
| 2735 | return rvalue(gz, rl, result, infix_node); | |
| 2736 | } | |
| 2737 | ||
| 2716 | 2738 | // TODO: is there an existing method to accomplish this? |
| 2717 | 2739 | // TODO: likely rename this to indicate rhs type coercion or add more params to make it more general |
| 2718 | 2740 | fn assignBinOpExt( |
| ... | ... | @@ -2720,8 +2742,8 @@ fn assignBinOpExt( |
| 2720 | 2742 | scope: *Scope, |
| 2721 | 2743 | infix_node: Ast.Node.Index, |
| 2722 | 2744 | op_inst_tag: Zir.Inst.Extended, |
| 2745 | comptime T: type, | |
| 2723 | 2746 | ) InnerError!void { |
| 2724 | try emitDbgNode(gz, infix_node); | |
| 2725 | 2747 | const astgen = gz.astgen; |
| 2726 | 2748 | const tree = astgen.tree; |
| 2727 | 2749 | const node_datas = tree.nodes.items(.data); |
| ... | ... | @@ -2730,7 +2752,7 @@ fn assignBinOpExt( |
| 2730 | 2752 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); |
| 2731 | 2753 | const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); |
| 2732 | 2754 | const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs); |
| 2733 | const result = try gz.addExtendedPayload(op_inst_tag, Zir.Inst.BinNode{ | |
| 2755 | const result = try gz.addExtendedPayload(op_inst_tag, T{ | |
| 2734 | 2756 | .node = gz.nodeIndexToRelative(infix_node), |
| 2735 | 2757 | .lhs = lhs, |
| 2736 | 2758 | .rhs = rhs, |
| ... | ... | @@ -7903,26 +7925,6 @@ fn shiftOp( |
| 7903 | 7925 | return rvalue(gz, rl, result, node); |
| 7904 | 7926 | } |
| 7905 | 7927 | |
| 7906 | // TODO: is there an existing way to do this? | |
| 7907 | // TODO: likely rename this to reflect result_loc == .none or add more params to make it more general | |
| 7908 | fn binOpExt( | |
| 7909 | gz: *GenZir, | |
| 7910 | scope: *Scope, | |
| 7911 | rl: ResultLoc, | |
| 7912 | node: Ast.Node.Index, | |
| 7913 | lhs_node: Ast.Node.Index, | |
| 7914 | rhs_node: Ast.Node.Index, | |
| 7915 | tag: Zir.Inst.Extended, | |
| 7916 | ) InnerError!Zir.Inst.Ref { | |
| 7917 | const lhs = try expr(gz, scope, .none, lhs_node); | |
| 7918 | const rhs = try expr(gz, scope, .none, rhs_node); | |
| 7919 | const result = try gz.addExtendedPayload(tag, Zir.Inst.Bin{ | |
| 7920 | .lhs = lhs, | |
| 7921 | .rhs = rhs, | |
| 7922 | }); | |
| 7923 | return rvalue(gz, rl, result, node); | |
| 7924 | } | |
| 7925 | ||
| 7926 | 7928 | fn cImport( |
| 7927 | 7929 | gz: *GenZir, |
| 7928 | 7930 | scope: *Scope, |
src/Sema.zig+17-8| ... | ... | @@ -694,10 +694,11 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 694 | 694 | .c_define => return sema.zirCDefine( block, extended), |
| 695 | 695 | .wasm_memory_size => return sema.zirWasmMemorySize( block, extended), |
| 696 | 696 | .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended), |
| 697 | .add_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 698 | .sub_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 699 | .mul_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 700 | .shl_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 697 | .add_with_saturation, | |
| 698 | .sub_with_saturation, | |
| 699 | .mul_with_saturation, | |
| 700 | .shl_with_saturation, | |
| 701 | => return sema.zirSatArithmetic( block, extended), | |
| 701 | 702 | // zig fmt: on |
| 702 | 703 | } |
| 703 | 704 | } |
| ... | ... | @@ -6163,7 +6164,7 @@ fn zirNegate( |
| 6163 | 6164 | const lhs = sema.resolveInst(.zero); |
| 6164 | 6165 | const rhs = sema.resolveInst(inst_data.operand); |
| 6165 | 6166 | |
| 6166 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | |
| 6167 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src, null); | |
| 6167 | 6168 | } |
| 6168 | 6169 | |
| 6169 | 6170 | fn zirArithmetic( |
| ... | ... | @@ -6183,7 +6184,7 @@ fn zirArithmetic( |
| 6183 | 6184 | const lhs = sema.resolveInst(extra.lhs); |
| 6184 | 6185 | const rhs = sema.resolveInst(extra.rhs); |
| 6185 | 6186 | |
| 6186 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); | |
| 6187 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, null); | |
| 6187 | 6188 | } |
| 6188 | 6189 | |
| 6189 | 6190 | fn zirOverflowArithmetic( |
| ... | ... | @@ -6209,10 +6210,17 @@ fn zirSatArithmetic( |
| 6209 | 6210 | defer tracy.end(); |
| 6210 | 6211 | |
| 6211 | 6212 | const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data; |
| 6212 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | |
| 6213 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirSatArithmetic", .{}); | |
| 6213 | sema.src = .{ .node_offset_bin_op = extra.node }; | |
| 6214 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = extra.node }; | |
| 6215 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = extra.node }; | |
| 6216 | const lhs = sema.resolveInst(extra.lhs); | |
| 6217 | const rhs = sema.resolveInst(extra.rhs); | |
| 6218 | ||
| 6219 | return sema.analyzeArithmetic(block, .extended, lhs, rhs, sema.src, lhs_src, rhs_src, extended); | |
| 6214 | 6220 | } |
| 6215 | 6221 | |
| 6222 | // TODO: audit - not sure if its a good idea to reuse this, adding `opt_extended` param | |
| 6223 | // FIXME: somehow, rhs of <<| is required to be Log2T. this should accept T | |
| 6216 | 6224 | fn analyzeArithmetic( |
| 6217 | 6225 | sema: *Sema, |
| 6218 | 6226 | block: *Scope.Block, |
| ... | ... | @@ -6223,6 +6231,7 @@ fn analyzeArithmetic( |
| 6223 | 6231 | src: LazySrcLoc, |
| 6224 | 6232 | lhs_src: LazySrcLoc, |
| 6225 | 6233 | rhs_src: LazySrcLoc, |
| 6234 | opt_extended: ?Zir.Inst.Extended.InstData, | |
| 6226 | 6235 | ) CompileError!Air.Inst.Ref { |
| 6227 | 6236 | const lhs_ty = sema.typeOf(lhs); |
| 6228 | 6237 | const rhs_ty = sema.typeOf(rhs); |
src/codegen/c.zig+117-3| ... | ... | @@ -885,17 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 885 | 885 | // that wrapping is UB. |
| 886 | 886 | .add, .ptr_add => try airBinOp( f, inst, " + "), |
| 887 | 887 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), |
| 888 | .addsat => return o.dg.fail("TODO: C backend: implement codegen for addsat", .{}), | |
| 888 | .addsat => return f.fail("TODO: C backend: implement codegen for addsat", .{}), | |
| 889 | 889 | // TODO use a different strategy for sub that communicates to the optimizer |
| 890 | 890 | // that wrapping is UB. |
| 891 | 891 | .sub, .ptr_sub => try airBinOp( f, inst, " - "), |
| 892 | 892 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), |
| 893 | .subsat => return o.dg.fail("TODO: C backend: implement codegen for subsat", .{}), | |
| 893 | .subsat => return f.fail("TODO: C backend: implement codegen for subsat", .{}), | |
| 894 | 894 | // TODO use a different strategy for mul that communicates to the optimizer |
| 895 | 895 | // that wrapping is UB. |
| 896 | 896 | .mul => try airBinOp( f, inst, " * "), |
| 897 | 897 | .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), |
| 898 | .mulsat => return o.dg.fail("TODO: C backend: implement codegen for mulsat", .{}), | |
| 898 | .mulsat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}), | |
| 899 | 899 | // TODO use a different strategy for div that communicates to the optimizer |
| 900 | 900 | // that wrapping is UB. |
| 901 | 901 | .div => try airBinOp( f, inst, " / "), |
| ... | ... | @@ -919,6 +919,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 919 | 919 | |
| 920 | 920 | .shr => try airBinOp(f, inst, " >> "), |
| 921 | 921 | .shl => try airBinOp(f, inst, " << "), |
| 922 | .shl_sat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}), | |
| 923 | ||
| 922 | 924 | |
| 923 | 925 | .not => try airNot( f, inst), |
| 924 | 926 | |
| ... | ... | @@ -1312,6 +1314,118 @@ fn airWrapOp( |
| 1312 | 1314 | return ret; |
| 1313 | 1315 | } |
| 1314 | 1316 | |
| 1317 | fn airSatOp( | |
| 1318 | o: *Object, | |
| 1319 | inst: Air.Inst.Index, | |
| 1320 | str_op: [*:0]const u8, | |
| 1321 | fn_op: [*:0]const u8, | |
| 1322 | ) !CValue { | |
| 1323 | if (o.liveness.isUnused(inst)) | |
| 1324 | return CValue.none; | |
| 1325 | ||
| 1326 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | |
| 1327 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1328 | const int_info = inst_ty.intInfo(o.dg.module.getTarget()); | |
| 1329 | const bits = int_info.bits; | |
| 1330 | ||
| 1331 | // if it's an unsigned int with non-arbitrary bit size then we can just add | |
| 1332 | const ok_bits = switch (bits) { | |
| 1333 | 8, 16, 32, 64, 128 => true, | |
| 1334 | else => false, | |
| 1335 | }; | |
| 1336 | ||
| 1337 | if (bits > 64) { | |
| 1338 | return f.fail("TODO: C backend: airSatOp for large integers", .{}); | |
| 1339 | } | |
| 1340 | ||
| 1341 | var min_buf: [80]u8 = undefined; | |
| 1342 | const min = switch (int_info.signedness) { | |
| 1343 | .unsigned => "0", | |
| 1344 | else => switch (inst_ty.tag()) { | |
| 1345 | .c_short => "SHRT_MIN", | |
| 1346 | .c_int => "INT_MIN", | |
| 1347 | .c_long => "LONG_MIN", | |
| 1348 | .c_longlong => "LLONG_MIN", | |
| 1349 | .isize => "INTPTR_MIN", | |
| 1350 | else => blk: { | |
| 1351 | const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); | |
| 1352 | break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { | |
| 1353 | error.NoSpaceLeft => unreachable, | |
| 1354 | else => |e| return e, | |
| 1355 | }; | |
| 1356 | }, | |
| 1357 | }, | |
| 1358 | }; | |
| 1359 | ||
| 1360 | var max_buf: [80]u8 = undefined; | |
| 1361 | const max = switch (inst_ty.tag()) { | |
| 1362 | .c_short => "SHRT_MAX", | |
| 1363 | .c_ushort => "USHRT_MAX", | |
| 1364 | .c_int => "INT_MAX", | |
| 1365 | .c_uint => "UINT_MAX", | |
| 1366 | .c_long => "LONG_MAX", | |
| 1367 | .c_ulong => "ULONG_MAX", | |
| 1368 | .c_longlong => "LLONG_MAX", | |
| 1369 | .c_ulonglong => "ULLONG_MAX", | |
| 1370 | .isize => "INTPTR_MAX", | |
| 1371 | .usize => "UINTPTR_MAX", | |
| 1372 | else => blk: { | |
| 1373 | const pow_bits = switch (int_info.signedness) { | |
| 1374 | .signed => bits - 1, | |
| 1375 | .unsigned => bits, | |
| 1376 | }; | |
| 1377 | const val = std.math.pow(u65, 2, pow_bits) - 1; | |
| 1378 | break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { | |
| 1379 | error.NoSpaceLeft => unreachable, | |
| 1380 | else => |e| return e, | |
| 1381 | }; | |
| 1382 | }, | |
| 1383 | }; | |
| 1384 | ||
| 1385 | const lhs = try o.resolveInst(bin_op.lhs); | |
| 1386 | const rhs = try o.resolveInst(bin_op.rhs); | |
| 1387 | const w = o.writer(); | |
| 1388 | ||
| 1389 | const ret = try o.allocLocal(inst_ty, .Mut); | |
| 1390 | try w.print(" = zig_{s}", .{fn_op}); | |
| 1391 | ||
| 1392 | switch (inst_ty.tag()) { | |
| 1393 | .isize => try w.writeAll("isize"), | |
| 1394 | .c_short => try w.writeAll("short"), | |
| 1395 | .c_int => try w.writeAll("int"), | |
| 1396 | .c_long => try w.writeAll("long"), | |
| 1397 | .c_longlong => try w.writeAll("longlong"), | |
| 1398 | else => { | |
| 1399 | const prefix_byte: u8 = switch (int_info.signedness) { | |
| 1400 | .signed => 'i', | |
| 1401 | .unsigned => 'u', | |
| 1402 | }; | |
| 1403 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { | |
| 1404 | if (bits <= nbits) { | |
| 1405 | try w.print("{c}{d}", .{ prefix_byte, nbits }); | |
| 1406 | break; | |
| 1407 | } | |
| 1408 | } else { | |
| 1409 | unreachable; | |
| 1410 | } | |
| 1411 | }, | |
| 1412 | } | |
| 1413 | ||
| 1414 | try w.writeByte('('); | |
| 1415 | try o.writeCValue(w, lhs); | |
| 1416 | try w.writeAll(", "); | |
| 1417 | try o.writeCValue(w, rhs); | |
| 1418 | ||
| 1419 | if (int_info.signedness == .signed) { | |
| 1420 | try w.print(", {s}", .{min}); | |
| 1421 | } | |
| 1422 | ||
| 1423 | try w.print(", {s});", .{max}); | |
| 1424 | try o.indent_writer.insertNewline(); | |
| 1425 | ||
| 1426 | return ret; | |
| 1427 | } | |
| 1428 | ||
| 1315 | 1429 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1316 | 1430 | if (f.liveness.isUnused(inst)) |
| 1317 | 1431 | return CValue.none; |
src/codegen/llvm.zig+3-3| ... | ... | @@ -2038,7 +2038,7 @@ pub const FuncGen = struct { |
| 2038 | 2038 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2039 | 2039 | const inst_ty = self.air.typeOfIndex(inst); |
| 2040 | 2040 | |
| 2041 | if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 2041 | if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 2042 | 2042 | if (ty == .wrapping) |
| 2043 | 2043 | return self.builder.buildAdd(lhs, rhs, "") |
| 2044 | 2044 | else if (ty == .saturated) { |
| ... | ... | @@ -2060,7 +2060,7 @@ pub const FuncGen = struct { |
| 2060 | 2060 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2061 | 2061 | const inst_ty = self.air.typeOfIndex(inst); |
| 2062 | 2062 | |
| 2063 | if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 2063 | if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 2064 | 2064 | if (ty == .wrapping) |
| 2065 | 2065 | return self.builder.buildSub(lhs, rhs, "") |
| 2066 | 2066 | else if (ty == .saturated) { |
| ... | ... | @@ -2082,7 +2082,7 @@ pub const FuncGen = struct { |
| 2082 | 2082 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2083 | 2083 | const inst_ty = self.air.typeOfIndex(inst); |
| 2084 | 2084 | |
| 2085 | if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 2085 | if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 2086 | 2086 | if (ty == .wrapping) |
| 2087 | 2087 | return self.builder.buildMul(lhs, rhs, "") |
| 2088 | 2088 | else if (ty == .saturated) { |
src/link/C/zig.h+93| ... | ... | @@ -356,3 +356,96 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon |
| 356 | 356 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); |
| 357 | 357 | } |
| 358 | 358 | |
| 359 | /* | |
| 360 | * Saturating aritmetic operations: add, sub, mul, shl | |
| 361 | */ | |
| 362 | #define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \ | |
| 363 | return (x > max - y) ? max : x + y; \ | |
| 364 | } | |
| 365 | ||
| 366 | #define zig_add_sat_s(ZT, T, T2) static inline T zig_adds_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 367 | T2 res = x + y; \ | |
| 368 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 369 | } | |
| 370 | ||
| 371 | zig_add_sat_u( u8, uint8_t) | |
| 372 | zig_add_sat_s( i8, int8_t, int16_t) | |
| 373 | zig_add_sat_u(u16, uint16_t) | |
| 374 | zig_add_sat_s(i16, int16_t, int32_t) | |
| 375 | zig_add_sat_u(u32, uint32_t) | |
| 376 | zig_add_sat_s(i32, int32_t, int64_t) | |
| 377 | zig_add_sat_u(u64, uint64_t) | |
| 378 | zig_add_sat_s(i64, int64_t, int128_t) | |
| 379 | zig_add_sat_s(isize, intptr_t, int128_t) | |
| 380 | zig_add_sat_s(short, short, int) | |
| 381 | zig_add_sat_s(int, int, long) | |
| 382 | zig_add_sat_s(long, long, long long) | |
| 383 | ||
| 384 | #define zig_sub_sat_u(ZT, T) static inline T zig_subs_##ZT(T x, T y, T max) { \ | |
| 385 | return (x > max + y) ? max : x - y; \ | |
| 386 | } | |
| 387 | ||
| 388 | #define zig_sub_sat_s(ZT, T, T2) static inline T zig_subs_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 389 | T2 res = x - y; \ | |
| 390 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 391 | } | |
| 392 | ||
| 393 | zig_sub_sat_u( u8, uint8_t) | |
| 394 | zig_sub_sat_s( i8, int8_t, int16_t) | |
| 395 | zig_sub_sat_u(u16, uint16_t) | |
| 396 | zig_sub_sat_s(i16, int16_t, int32_t) | |
| 397 | zig_sub_sat_u(u32, uint32_t) | |
| 398 | zig_sub_sat_s(i32, int32_t, int64_t) | |
| 399 | zig_sub_sat_u(u64, uint64_t) | |
| 400 | zig_sub_sat_s(i64, int64_t, int128_t) | |
| 401 | zig_sub_sat_s(isize, intptr_t, int128_t) | |
| 402 | zig_sub_sat_s(short, short, int) | |
| 403 | zig_sub_sat_s(int, int, long) | |
| 404 | zig_sub_sat_s(long, long, long long) | |
| 405 | ||
| 406 | ||
| 407 | #define zig_mul_sat_u(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 max) { \ | |
| 408 | T2 res = x * y; \ | |
| 409 | return (res > max) ? max : res; \ | |
| 410 | } | |
| 411 | ||
| 412 | #define zig_mul_sat_s(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 413 | T2 res = x * y; \ | |
| 414 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 415 | } | |
| 416 | ||
| 417 | zig_mul_sat_u(u8, uint8_t, uint16_t) | |
| 418 | zig_mul_sat_s(i8, int8_t, int16_t) | |
| 419 | zig_mul_sat_u(u16, uint16_t, uint32_t) | |
| 420 | zig_mul_sat_s(i16, int16_t, int32_t) | |
| 421 | zig_mul_sat_u(u32, uint32_t, uint64_t) | |
| 422 | zig_mul_sat_s(i32, int32_t, int64_t) | |
| 423 | zig_mul_sat_u(u64, uint64_t, uint128_t) | |
| 424 | zig_mul_sat_s(i64, int64_t, int128_t) | |
| 425 | zig_mul_sat_s(isize, intptr_t, int128_t) | |
| 426 | zig_mul_sat_s(short, short, int) | |
| 427 | zig_mul_sat_s(int, int, long) | |
| 428 | zig_mul_sat_s(long, long, long long) | |
| 429 | ||
| 430 | #define zig_shl_sat_u(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T max) { \ | |
| 431 | T leading_zeros = __builtin_clz(x); \ | |
| 432 | return (leading_zeros + y > bits) ? max : x << y; \ | |
| 433 | } | |
| 434 | ||
| 435 | #define zig_shl_sat_s(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T min, T max) { \ | |
| 436 | T leading_zeros = __builtin_clz(x & ~max); \ | |
| 437 | return (leading_zeros + y > bits) ? max : x << y; \ | |
| 438 | } | |
| 439 | ||
| 440 | zig_shl_sat_u(u8, uint8_t, 8) | |
| 441 | zig_shl_sat_s(i8, int8_t, 7) | |
| 442 | zig_shl_sat_u(u16, uint16_t, 16) | |
| 443 | zig_shl_sat_s(i16, int16_t, 15) | |
| 444 | zig_shl_sat_u(u32, uint32_t, 32) | |
| 445 | zig_shl_sat_s(i32, int32_t, 31) | |
| 446 | zig_shl_sat_u(u64, uint64_t, 64) | |
| 447 | zig_shl_sat_s(i64, int64_t, 63) | |
| 448 | zig_shl_sat_s(isize, intptr_t, 63) | |
| 449 | zig_shl_sat_s(short, short, 15) | |
| 450 | zig_shl_sat_s(int, int, 31) | |
| 451 | zig_shl_sat_s(long, long, 63) | |
| \ No newline at end of file |