authorgravatar for twostepted@gmail.comTravis Staloch <twostepted@gmail.com> 2021-09-08 15:19:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 17:03:41-07:00
logb9a95f2dd94e6175322d3388c3936eb600ec90ea
tree186a31fd920ca47bc15c3a426b9dfa8d67756ada
parent29f41896ed9d99e82a88f4b63efa182ca0d2f93c

sat-arithmetic: add c backend support

- 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.h

5 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,7 +535,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
535 return rvalue(gz, rl, .void_value, node);535 return rvalue(gz, rl, .void_value, node);
536 },536 },
537 .assign_bit_shift_left_sat => {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 return rvalue(gz, rl, .void_value, node);539 return rvalue(gz, rl, .void_value, node);
540 },540 },
541 .assign_bit_shift_right => {541 .assign_bit_shift_right => {
...@@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
568 return rvalue(gz, rl, .void_value, node);568 return rvalue(gz, rl, .void_value, node);
569 },569 },
570 .assign_sub_sat => {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 return rvalue(gz, rl, .void_value, node);572 return rvalue(gz, rl, .void_value, node);
573 },573 },
574 .assign_mod => {574 .assign_mod => {
...@@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
584 return rvalue(gz, rl, .void_value, node);584 return rvalue(gz, rl, .void_value, node);
585 },585 },
586 .assign_add_sat => {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 return rvalue(gz, rl, .void_value, node);588 return rvalue(gz, rl, .void_value, node);
589 },589 },
590 .assign_mul => {590 .assign_mul => {
...@@ -596,24 +596,24 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -596,24 +596,24 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
596 return rvalue(gz, rl, .void_value, node);596 return rvalue(gz, rl, .void_value, node);
597 },597 },
598 .assign_mul_sat => {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 return rvalue(gz, rl, .void_value, node);600 return rvalue(gz, rl, .void_value, node);
601 },601 },
602602
603 // zig fmt: off603 // zig fmt: off
604 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),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 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),606 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
607607
608 .add => return simpleBinOp(gz, scope, rl, node, .add),608 .add => return simpleBinOp(gz, scope, rl, node, .add),
609 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),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 .sub => return simpleBinOp(gz, scope, rl, node, .sub),611 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
612 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),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 .mul => return simpleBinOp(gz, scope, rl, node, .mul),614 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
615 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),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 .div => return simpleBinOp(gz, scope, rl, node, .div),617 .div => return simpleBinOp(gz, scope, rl, node, .div),
618 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),618 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
619 .bit_and => {619 .bit_and => {
...@@ -2713,6 +2713,28 @@ fn assignOp(...@@ -2713,6 +2713,28 @@ fn assignOp(
2713 _ = try gz.addBin(.store, lhs_ptr, result);2713 _ = try gz.addBin(.store, lhs_ptr, result);
2714}2714}
27152715
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
2718fn 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// TODO: is there an existing method to accomplish this?2738// TODO: is there an existing method to accomplish this?
2717// TODO: likely rename this to indicate rhs type coercion or add more params to make it more general2739// TODO: likely rename this to indicate rhs type coercion or add more params to make it more general
2718fn assignBinOpExt(2740fn assignBinOpExt(
...@@ -2720,8 +2742,8 @@ fn assignBinOpExt(...@@ -2720,8 +2742,8 @@ fn assignBinOpExt(
2720 scope: *Scope,2742 scope: *Scope,
2721 infix_node: Ast.Node.Index,2743 infix_node: Ast.Node.Index,
2722 op_inst_tag: Zir.Inst.Extended,2744 op_inst_tag: Zir.Inst.Extended,
2745 comptime T: type,
2723) InnerError!void {2746) InnerError!void {
2724 try emitDbgNode(gz, infix_node);
2725 const astgen = gz.astgen;2747 const astgen = gz.astgen;
2726 const tree = astgen.tree;2748 const tree = astgen.tree;
2727 const node_datas = tree.nodes.items(.data);2749 const node_datas = tree.nodes.items(.data);
...@@ -2730,7 +2752,7 @@ fn assignBinOpExt(...@@ -2730,7 +2752,7 @@ fn assignBinOpExt(
2730 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);2752 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2731 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);2753 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
2732 const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);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 .node = gz.nodeIndexToRelative(infix_node),2756 .node = gz.nodeIndexToRelative(infix_node),
2735 .lhs = lhs,2757 .lhs = lhs,
2736 .rhs = rhs,2758 .rhs = rhs,
...@@ -7903,26 +7925,6 @@ fn shiftOp(...@@ -7903,26 +7925,6 @@ fn shiftOp(
7903 return rvalue(gz, rl, result, node);7925 return rvalue(gz, rl, result, node);
7904}7926}
79057927
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
7908fn 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
7926fn cImport(7928fn cImport(
7927 gz: *GenZir,7929 gz: *GenZir,
7928 scope: *Scope,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,10 +694,11 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
694 .c_define => return sema.zirCDefine( block, extended),694 .c_define => return sema.zirCDefine( block, extended),
695 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),695 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
696 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),696 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
697 .add_with_saturation=> return sema.zirSatArithmetic( block, extended),697 .add_with_saturation,
698 .sub_with_saturation=> return sema.zirSatArithmetic( block, extended),698 .sub_with_saturation,
699 .mul_with_saturation=> return sema.zirSatArithmetic( block, extended),699 .mul_with_saturation,
700 .shl_with_saturation=> return sema.zirSatArithmetic( block, extended),700 .shl_with_saturation,
701 => return sema.zirSatArithmetic( block, extended),
701 // zig fmt: on702 // zig fmt: on
702 }703 }
703}704}
...@@ -6163,7 +6164,7 @@ fn zirNegate(...@@ -6163,7 +6164,7 @@ fn zirNegate(
6163 const lhs = sema.resolveInst(.zero);6164 const lhs = sema.resolveInst(.zero);
6164 const rhs = sema.resolveInst(inst_data.operand);6165 const rhs = sema.resolveInst(inst_data.operand);
61656166
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}
61686169
6169fn zirArithmetic(6170fn zirArithmetic(
...@@ -6183,7 +6184,7 @@ fn zirArithmetic(...@@ -6183,7 +6184,7 @@ fn zirArithmetic(
6183 const lhs = sema.resolveInst(extra.lhs);6184 const lhs = sema.resolveInst(extra.lhs);
6184 const rhs = sema.resolveInst(extra.rhs);6185 const rhs = sema.resolveInst(extra.rhs);
61856186
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}
61886189
6189fn zirOverflowArithmetic(6190fn zirOverflowArithmetic(
...@@ -6209,10 +6210,17 @@ fn zirSatArithmetic(...@@ -6209,10 +6210,17 @@ fn zirSatArithmetic(
6209 defer tracy.end();6210 defer tracy.end();
62106211
6211 const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;6212 const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;
6212 const src: LazySrcLoc = .{ .node_offset = extra.node };6213 sema.src = .{ .node_offset_bin_op = extra.node };
6213 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirSatArithmetic", .{});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}
62156221
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
6216fn analyzeArithmetic(6224fn analyzeArithmetic(
6217 sema: *Sema,6225 sema: *Sema,
6218 block: *Scope.Block,6226 block: *Scope.Block,
...@@ -6223,6 +6231,7 @@ fn analyzeArithmetic(...@@ -6223,6 +6231,7 @@ fn analyzeArithmetic(
6223 src: LazySrcLoc,6231 src: LazySrcLoc,
6224 lhs_src: LazySrcLoc,6232 lhs_src: LazySrcLoc,
6225 rhs_src: LazySrcLoc,6233 rhs_src: LazySrcLoc,
6234 opt_extended: ?Zir.Inst.Extended.InstData,
6226) CompileError!Air.Inst.Ref {6235) CompileError!Air.Inst.Ref {
6227 const lhs_ty = sema.typeOf(lhs);6236 const lhs_ty = sema.typeOf(lhs);
6228 const rhs_ty = sema.typeOf(rhs);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,17 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
885 // that wrapping is UB.885 // that wrapping is UB.
886 .add, .ptr_add => try airBinOp( f, inst, " + "),886 .add, .ptr_add => try airBinOp( f, inst, " + "),
887 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),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 // TODO use a different strategy for sub that communicates to the optimizer889 // TODO use a different strategy for sub that communicates to the optimizer
890 // that wrapping is UB.890 // that wrapping is UB.
891 .sub, .ptr_sub => try airBinOp( f, inst, " - "),891 .sub, .ptr_sub => try airBinOp( f, inst, " - "),
892 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),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 // TODO use a different strategy for mul that communicates to the optimizer894 // TODO use a different strategy for mul that communicates to the optimizer
895 // that wrapping is UB.895 // that wrapping is UB.
896 .mul => try airBinOp( f, inst, " * "),896 .mul => try airBinOp( f, inst, " * "),
897 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),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 // TODO use a different strategy for div that communicates to the optimizer899 // TODO use a different strategy for div that communicates to the optimizer
900 // that wrapping is UB.900 // that wrapping is UB.
901 .div => try airBinOp( f, inst, " / "),901 .div => try airBinOp( f, inst, " / "),
...@@ -919,6 +919,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -919,6 +919,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
919919
920 .shr => try airBinOp(f, inst, " >> "),920 .shr => try airBinOp(f, inst, " >> "),
921 .shl => try airBinOp(f, inst, " << "),921 .shl => try airBinOp(f, inst, " << "),
922 .shl_sat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}),
923
922924
923 .not => try airNot( f, inst),925 .not => try airNot( f, inst),
924926
...@@ -1312,6 +1314,118 @@ fn airWrapOp(...@@ -1312,6 +1314,118 @@ fn airWrapOp(
1312 return ret;1314 return ret;
1313}1315}
13141316
1317fn 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
1315fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {1429fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
1316 if (f.liveness.isUnused(inst))1430 if (f.liveness.isUnused(inst))
1317 return CValue.none;1431 return CValue.none;
src/codegen/llvm.zig+3-3
...@@ -2038,7 +2038,7 @@ pub const FuncGen = struct {...@@ -2038,7 +2038,7 @@ pub const FuncGen = struct {
2038 const rhs = try self.resolveInst(bin_op.rhs);2038 const rhs = try self.resolveInst(bin_op.rhs);
2039 const inst_ty = self.air.typeOfIndex(inst);2039 const inst_ty = self.air.typeOfIndex(inst);
20402040
2041 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");2041 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2042 if (ty == .wrapping)2042 if (ty == .wrapping)
2043 return self.builder.buildAdd(lhs, rhs, "")2043 return self.builder.buildAdd(lhs, rhs, "")
2044 else if (ty == .saturated) {2044 else if (ty == .saturated) {
...@@ -2060,7 +2060,7 @@ pub const FuncGen = struct {...@@ -2060,7 +2060,7 @@ pub const FuncGen = struct {
2060 const rhs = try self.resolveInst(bin_op.rhs);2060 const rhs = try self.resolveInst(bin_op.rhs);
2061 const inst_ty = self.air.typeOfIndex(inst);2061 const inst_ty = self.air.typeOfIndex(inst);
20622062
2063 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");2063 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
2064 if (ty == .wrapping)2064 if (ty == .wrapping)
2065 return self.builder.buildSub(lhs, rhs, "")2065 return self.builder.buildSub(lhs, rhs, "")
2066 else if (ty == .saturated) {2066 else if (ty == .saturated) {
...@@ -2082,7 +2082,7 @@ pub const FuncGen = struct {...@@ -2082,7 +2082,7 @@ pub const FuncGen = struct {
2082 const rhs = try self.resolveInst(bin_op.rhs);2082 const rhs = try self.resolveInst(bin_op.rhs);
2083 const inst_ty = self.air.typeOfIndex(inst);2083 const inst_ty = self.air.typeOfIndex(inst);
20842084
2085 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");2085 if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
2086 if (ty == .wrapping)2086 if (ty == .wrapping)
2087 return self.builder.buildMul(lhs, rhs, "")2087 return self.builder.buildMul(lhs, rhs, "")
2088 else if (ty == .saturated) {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,3 +356,96 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
357}357}
358358
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
371zig_add_sat_u( u8, uint8_t)
372zig_add_sat_s( i8, int8_t, int16_t)
373zig_add_sat_u(u16, uint16_t)
374zig_add_sat_s(i16, int16_t, int32_t)
375zig_add_sat_u(u32, uint32_t)
376zig_add_sat_s(i32, int32_t, int64_t)
377zig_add_sat_u(u64, uint64_t)
378zig_add_sat_s(i64, int64_t, int128_t)
379zig_add_sat_s(isize, intptr_t, int128_t)
380zig_add_sat_s(short, short, int)
381zig_add_sat_s(int, int, long)
382zig_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
393zig_sub_sat_u( u8, uint8_t)
394zig_sub_sat_s( i8, int8_t, int16_t)
395zig_sub_sat_u(u16, uint16_t)
396zig_sub_sat_s(i16, int16_t, int32_t)
397zig_sub_sat_u(u32, uint32_t)
398zig_sub_sat_s(i32, int32_t, int64_t)
399zig_sub_sat_u(u64, uint64_t)
400zig_sub_sat_s(i64, int64_t, int128_t)
401zig_sub_sat_s(isize, intptr_t, int128_t)
402zig_sub_sat_s(short, short, int)
403zig_sub_sat_s(int, int, long)
404zig_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
417zig_mul_sat_u(u8, uint8_t, uint16_t)
418zig_mul_sat_s(i8, int8_t, int16_t)
419zig_mul_sat_u(u16, uint16_t, uint32_t)
420zig_mul_sat_s(i16, int16_t, int32_t)
421zig_mul_sat_u(u32, uint32_t, uint64_t)
422zig_mul_sat_s(i32, int32_t, int64_t)
423zig_mul_sat_u(u64, uint64_t, uint128_t)
424zig_mul_sat_s(i64, int64_t, int128_t)
425zig_mul_sat_s(isize, intptr_t, int128_t)
426zig_mul_sat_s(short, short, int)
427zig_mul_sat_s(int, int, long)
428zig_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
440zig_shl_sat_u(u8, uint8_t, 8)
441zig_shl_sat_s(i8, int8_t, 7)
442zig_shl_sat_u(u16, uint16_t, 16)
443zig_shl_sat_s(i16, int16_t, 15)
444zig_shl_sat_u(u32, uint32_t, 32)
445zig_shl_sat_s(i32, int32_t, 31)
446zig_shl_sat_u(u64, uint64_t, 64)
447zig_shl_sat_s(i64, int64_t, 63)
448zig_shl_sat_s(isize, intptr_t, 63)
449zig_shl_sat_s(short, short, 15)
450zig_shl_sat_s(int, int, 31)
451zig_shl_sat_s(long, long, 63)
\ No newline at end of file