1//! This file encapsules all arithmetic operations on comptime-known integers, floats, and vectors.
2//!
3//! It is only used in cases where both operands are comptime-known; a single comptime-known operand
4//! is handled directly by `Sema.zig`.
5//!
6//! All public functions sanitize their inputs to the best of their knowledge.
7//!
8//! Functions starting with `int`, `comptimeInt`, or `float` are low-level primitives which operate
9//! on defined scalar values; generally speaking, they are at the bottom of this file and non-`pub`.
10
11/// Asserts that `ty` is a scalar integer type, and that `prev_val` is of type `ty`.
12/// Returns a value one greater than `prev_val`. If this would overflow `ty,` then the
13/// return value has `overflow` set, and `val` is instead a `comptime_int`.
14pub fn incrementDefinedInt(
15 sema: *Sema,
16 ty: Type,
17 prev_val: Value,
18) CompileError!struct { overflow: bool, val: Value } {
19 const pt = sema.pt;
20 const zcu = pt.zcu;
21 assert(prev_val.typeOf(zcu).toIntern() == ty.toIntern());
22 assert(!prev_val.isUndef(zcu));
23 if (ty.toIntern() == .u0_type) {
24 return .{ .overflow = true, .val = try comptimeIntAdd(sema, prev_val, .one_comptime_int) };
25 }
26 const res = try intAdd(sema, prev_val, try pt.intValue(ty, 1), ty);
27 return .{ .overflow = res.overflow, .val = res.val };
28}
29
30/// `val` is of type `ty`.
31/// `ty` is a float, comptime_float, or vector thereof.
32pub fn negateFloat(
33 sema: *Sema,
34 ty: Type,
35 val: Value,
36) CompileError!Value {
37 const pt = sema.pt;
38 const zcu = pt.zcu;
39 if (val.isUndef(zcu)) return val;
40 switch (ty.zigTypeTag(zcu)) {
41 .vector => {
42 const scalar_ty = ty.childType(zcu);
43 const len = ty.vectorLen(zcu);
44 const result_elems = try sema.arena.alloc(InternPool.Index, len);
45 for (result_elems, 0..) |*result_elem, elem_idx| {
46 const elem = try val.elemValue(pt, elem_idx);
47 if (elem.isUndef(zcu)) {
48 result_elem.* = elem.toIntern();
49 } else {
50 result_elem.* = (try floatNeg(sema, elem, scalar_ty)).toIntern();
51 }
52 }
53 return pt.aggregateValue(ty, result_elems);
54 },
55 .float, .comptime_float => return floatNeg(sema, val, ty),
56 else => unreachable,
57 }
58}
59
60/// Wraps on integers, but accepts floats.
61/// `lhs_val` and `rhs_val` are both of type `ty`.
62/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
63pub fn addMaybeWrap(
64 sema: *Sema,
65 ty: Type,
66 lhs: Value,
67 rhs: Value,
68) CompileError!Value {
69 const zcu = sema.pt.zcu;
70 if (lhs.isUndef(zcu)) return lhs;
71 if (rhs.isUndef(zcu)) return rhs;
72 switch (ty.zigTypeTag(zcu)) {
73 .int, .comptime_int => return (try intAddWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
74 .float, .comptime_float => return floatAdd(sema, lhs, rhs, ty),
75 else => unreachable,
76 }
77}
78
79/// Wraps on integers, but accepts floats.
80/// `lhs_val` and `rhs_val` are both of type `ty`.
81/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
82pub fn subMaybeWrap(
83 sema: *Sema,
84 ty: Type,
85 lhs: Value,
86 rhs: Value,
87) CompileError!Value {
88 const zcu = sema.pt.zcu;
89 if (lhs.isUndef(zcu)) return lhs;
90 if (rhs.isUndef(zcu)) return rhs;
91 switch (ty.zigTypeTag(zcu)) {
92 .int, .comptime_int => return (try intSubWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
93 .float, .comptime_float => return floatSub(sema, lhs, rhs, ty),
94 else => unreachable,
95 }
96}
97
98/// Wraps on integers, but accepts floats.
99/// `lhs_val` and `rhs_val` are both of type `ty`.
100/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
101pub fn mulMaybeWrap(
102 sema: *Sema,
103 ty: Type,
104 lhs: Value,
105 rhs: Value,
106) CompileError!Value {
107 const zcu = sema.pt.zcu;
108 if (lhs.isUndef(zcu)) return lhs;
109 if (rhs.isUndef(zcu)) return rhs;
110 switch (ty.zigTypeTag(zcu)) {
111 .int, .comptime_int => return (try intMulWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
112 .float, .comptime_float => return floatMul(sema, lhs, rhs, ty),
113 else => unreachable,
114 }
115}
116
117/// `lhs` and `rhs` are of type `ty`.
118/// `ty` is an int, comptime_int, or vector thereof.
119pub fn addWithOverflow(
120 sema: *Sema,
121 ty: Type,
122 lhs: Value,
123 rhs: Value,
124) CompileError!Value.OverflowArithmeticResult {
125 const pt = sema.pt;
126 const zcu = pt.zcu;
127 switch (ty.zigTypeTag(zcu)) {
128 .int, .comptime_int => return addWithOverflowScalar(sema, ty, lhs, rhs),
129 .vector => {
130 const scalar_ty = ty.childType(zcu);
131 const len = ty.vectorLen(zcu);
132 switch (scalar_ty.zigTypeTag(zcu)) {
133 .int, .comptime_int => {},
134 else => unreachable,
135 }
136 const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
137 const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
138 for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
139 const lhs_elem = try lhs.elemValue(pt, elem_idx);
140 const rhs_elem = try rhs.elemValue(pt, elem_idx);
141 const elem_result = try addWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
142 ob.* = elem_result.overflow_bit.toIntern();
143 wr.* = elem_result.wrapped_result.toIntern();
144 }
145 return .{
146 .overflow_bit = try pt.aggregateValue(
147 try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
148 overflow_bits,
149 ),
150 .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
151 };
152 },
153 else => unreachable,
154 }
155}
156fn addWithOverflowScalar(
157 sema: *Sema,
158 ty: Type,
159 lhs: Value,
160 rhs: Value,
161) CompileError!Value.OverflowArithmeticResult {
162 const pt = sema.pt;
163 const zcu = pt.zcu;
164 switch (ty.zigTypeTag(zcu)) {
165 .int, .comptime_int => {},
166 else => unreachable,
167 }
168 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
169 .overflow_bit = .undef_u1,
170 .wrapped_result = try pt.undefValue(ty),
171 };
172 const res = try intAddWithOverflow(sema, lhs, rhs, ty);
173 return .{
174 .overflow_bit = if (res.overflow) .one_u1 else .zero_u1,
175 .wrapped_result = res.wrapped_result,
176 };
177}
178
179/// `lhs` and `rhs` are of type `ty`.
180/// `ty` is an int, comptime_int, or vector thereof.
181pub fn subWithOverflow(
182 sema: *Sema,
183 ty: Type,
184 lhs: Value,
185 rhs: Value,
186) CompileError!Value.OverflowArithmeticResult {
187 const pt = sema.pt;
188 const zcu = pt.zcu;
189 switch (ty.zigTypeTag(zcu)) {
190 .int, .comptime_int => return subWithOverflowScalar(sema, ty, lhs, rhs),
191 .vector => {
192 const scalar_ty = ty.childType(zcu);
193 const len = ty.vectorLen(zcu);
194 switch (scalar_ty.zigTypeTag(zcu)) {
195 .int, .comptime_int => {},
196 else => unreachable,
197 }
198 const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
199 const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
200 for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
201 const lhs_elem = try lhs.elemValue(pt, elem_idx);
202 const rhs_elem = try rhs.elemValue(pt, elem_idx);
203 const elem_result = try subWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
204 ob.* = elem_result.overflow_bit.toIntern();
205 wr.* = elem_result.wrapped_result.toIntern();
206 }
207 return .{
208 .overflow_bit = try pt.aggregateValue(
209 try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
210 overflow_bits,
211 ),
212 .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
213 };
214 },
215 else => unreachable,
216 }
217}
218fn subWithOverflowScalar(
219 sema: *Sema,
220 ty: Type,
221 lhs: Value,
222 rhs: Value,
223) CompileError!Value.OverflowArithmeticResult {
224 const pt = sema.pt;
225 const zcu = pt.zcu;
226 switch (ty.zigTypeTag(zcu)) {
227 .int, .comptime_int => {},
228 else => unreachable,
229 }
230 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
231 .overflow_bit = .undef_u1,
232 .wrapped_result = try pt.undefValue(ty),
233 };
234
235 const res = try intSubWithOverflow(sema, lhs, rhs, ty);
236 return .{
237 .overflow_bit = if (res.overflow) .one_u1 else .zero_u1,
238 .wrapped_result = res.wrapped_result,
239 };
240}
241
242/// `lhs` and `rhs` are of type `ty`.
243/// `ty` is an int, comptime_int, or vector thereof.
244pub fn mulWithOverflow(
245 sema: *Sema,
246 ty: Type,
247 lhs: Value,
248 rhs: Value,
249) CompileError!Value.OverflowArithmeticResult {
250 const pt = sema.pt;
251 const zcu = pt.zcu;
252 switch (ty.zigTypeTag(zcu)) {
253 .int, .comptime_int => return mulWithOverflowScalar(sema, ty, lhs, rhs),
254 .vector => {
255 const scalar_ty = ty.childType(zcu);
256 const len = ty.vectorLen(zcu);
257 switch (scalar_ty.zigTypeTag(zcu)) {
258 .int, .comptime_int => {},
259 else => unreachable,
260 }
261 const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
262 const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
263 for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
264 const lhs_elem = try lhs.elemValue(pt, elem_idx);
265 const rhs_elem = try rhs.elemValue(pt, elem_idx);
266 const elem_result = try mulWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
267 ob.* = elem_result.overflow_bit.toIntern();
268 wr.* = elem_result.wrapped_result.toIntern();
269 }
270 return .{
271 .overflow_bit = try pt.aggregateValue(
272 try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
273 overflow_bits,
274 ),
275 .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
276 };
277 },
278 else => unreachable,
279 }
280}
281fn mulWithOverflowScalar(
282 sema: *Sema,
283 ty: Type,
284 lhs: Value,
285 rhs: Value,
286) CompileError!Value.OverflowArithmeticResult {
287 const pt = sema.pt;
288 const zcu = pt.zcu;
289 switch (ty.zigTypeTag(zcu)) {
290 .int, .comptime_int => {},
291 else => unreachable,
292 }
293 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
294 .overflow_bit = .undef_u1,
295 .wrapped_result = try pt.undefValue(ty),
296 };
297 const res = try intMulWithOverflow(sema, lhs, rhs, ty);
298 return .{
299 .overflow_bit = if (res.overflow) .one_u1 else .zero_u1,
300 .wrapped_result = res.wrapped_result,
301 };
302}
303
304/// Applies the `+` operator to comptime-known values.
305/// `lhs_val` and `rhs_val` are both of type `ty`.
306/// `ty` is an int, float, comptime_int, comptime_float, or vector.
307pub fn add(
308 sema: *Sema,
309 block: *Block,
310 ty: Type,
311 lhs_val: Value,
312 rhs_val: Value,
313 src: LazySrcLoc,
314 lhs_src: LazySrcLoc,
315 rhs_src: LazySrcLoc,
316) CompileError!Value {
317 const pt = sema.pt;
318 const zcu = pt.zcu;
319 switch (ty.zigTypeTag(zcu)) {
320 .int, .comptime_int => return addScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
321 .float, .comptime_float => return addScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
322 .vector => {
323 const elem_ty = ty.childType(zcu);
324 const len = ty.vectorLen(zcu);
325
326 const is_int = switch (elem_ty.zigTypeTag(zcu)) {
327 .int, .comptime_int => true,
328 .float, .comptime_float => false,
329 else => unreachable,
330 };
331
332 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
333 for (elem_vals, 0..) |*result_elem, elem_idx| {
334 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
335 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
336 result_elem.* = (try addScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
337 }
338 return pt.aggregateValue(ty, elem_vals);
339 },
340 else => unreachable,
341 }
342}
343fn addScalar(
344 sema: *Sema,
345 block: *Block,
346 ty: Type,
347 lhs_val: Value,
348 rhs_val: Value,
349 src: LazySrcLoc,
350 lhs_src: LazySrcLoc,
351 rhs_src: LazySrcLoc,
352 is_int: bool,
353 vec_idx: ?usize,
354) CompileError!Value {
355 const pt = sema.pt;
356 const zcu = pt.zcu;
357
358 if (is_int) {
359 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
360 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
361 const res = try intAdd(sema, lhs_val, rhs_val, ty);
362 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
363 return res.val;
364 } else {
365 if (lhs_val.isUndef(zcu)) return lhs_val;
366 if (rhs_val.isUndef(zcu)) return rhs_val;
367 return floatAdd(sema, lhs_val, rhs_val, ty);
368 }
369}
370
371/// Applies the `+%` operator to comptime-known values.
372/// `lhs_val` and `rhs_val` are both of type `ty`.
373/// `ty` is an int, comptime_int, or vector thereof.
374pub fn addWrap(
375 sema: *Sema,
376 ty: Type,
377 lhs_val: Value,
378 rhs_val: Value,
379) CompileError!Value {
380 const pt = sema.pt;
381 const zcu = pt.zcu;
382 switch (ty.zigTypeTag(zcu)) {
383 .vector => {
384 const elem_ty = ty.childType(zcu);
385 const len = ty.vectorLen(zcu);
386
387 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
388 for (elem_vals, 0..) |*result_elem, elem_idx| {
389 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
390 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
391 result_elem.* = (try addWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
392 }
393 return pt.aggregateValue(ty, elem_vals);
394 },
395 else => return addWrapScalar(sema, ty, lhs_val, rhs_val),
396 }
397}
398fn addWrapScalar(
399 sema: *Sema,
400 ty: Type,
401 lhs_val: Value,
402 rhs_val: Value,
403) CompileError!Value {
404 return (try addWithOverflowScalar(sema, ty, lhs_val, rhs_val)).wrapped_result;
405}
406
407/// Applies the `+|` operator to comptime-known values.
408/// `lhs_val` and `rhs_val` are both of type `ty`.
409/// `ty` is an int, comptime_int, or vector thereof.
410pub fn addSat(
411 sema: *Sema,
412 ty: Type,
413 lhs_val: Value,
414 rhs_val: Value,
415) CompileError!Value {
416 const pt = sema.pt;
417 const zcu = pt.zcu;
418 switch (ty.zigTypeTag(zcu)) {
419 .vector => {
420 const elem_ty = ty.childType(zcu);
421 const len = ty.vectorLen(zcu);
422
423 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
424 for (elem_vals, 0..) |*result_elem, elem_idx| {
425 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
426 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
427 result_elem.* = (try addSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
428 }
429 return pt.aggregateValue(ty, elem_vals);
430 },
431 else => return addSatScalar(sema, ty, lhs_val, rhs_val),
432 }
433}
434fn addSatScalar(
435 sema: *Sema,
436 ty: Type,
437 lhs_val: Value,
438 rhs_val: Value,
439) CompileError!Value {
440 const pt = sema.pt;
441 const zcu = pt.zcu;
442 const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
443 .int => false,
444 .comptime_int => true,
445 else => unreachable,
446 };
447 if (lhs_val.isUndef(zcu)) return lhs_val;
448 if (rhs_val.isUndef(zcu)) return rhs_val;
449 if (is_comptime_int) {
450 const res = try intAdd(sema, lhs_val, rhs_val, ty);
451 assert(!res.overflow);
452 return res.val;
453 } else {
454 return intAddSat(sema, lhs_val, rhs_val, ty);
455 }
456}
457
458/// Applies the `-` operator to comptime-known values.
459/// `lhs_val` and `rhs_val` are both of type `ty`.
460/// `ty` is an int, float, comptime_int, comptime_float, or vector.
461pub fn sub(
462 sema: *Sema,
463 block: *Block,
464 ty: Type,
465 lhs_val: Value,
466 rhs_val: Value,
467 src: LazySrcLoc,
468 lhs_src: LazySrcLoc,
469 rhs_src: LazySrcLoc,
470) CompileError!Value {
471 const pt = sema.pt;
472 const zcu = pt.zcu;
473 switch (ty.zigTypeTag(zcu)) {
474 .int, .comptime_int => return subScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
475 .float, .comptime_float => return subScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
476 .vector => {
477 const elem_ty = ty.childType(zcu);
478 const len = ty.vectorLen(zcu);
479
480 const is_int = switch (elem_ty.zigTypeTag(zcu)) {
481 .int, .comptime_int => true,
482 .float, .comptime_float => false,
483 else => unreachable,
484 };
485
486 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
487 for (elem_vals, 0..) |*result_elem, elem_idx| {
488 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
489 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
490 result_elem.* = (try subScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
491 }
492 return pt.aggregateValue(ty, elem_vals);
493 },
494 else => unreachable,
495 }
496}
497fn subScalar(
498 sema: *Sema,
499 block: *Block,
500 ty: Type,
501 lhs_val: Value,
502 rhs_val: Value,
503 src: LazySrcLoc,
504 lhs_src: LazySrcLoc,
505 rhs_src: LazySrcLoc,
506 is_int: bool,
507 vec_idx: ?usize,
508) CompileError!Value {
509 const pt = sema.pt;
510 const zcu = pt.zcu;
511
512 if (is_int) {
513 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
514 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
515 const res = try intSub(sema, lhs_val, rhs_val, ty);
516 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
517 return res.val;
518 } else {
519 if (lhs_val.isUndef(zcu)) return lhs_val;
520 if (rhs_val.isUndef(zcu)) return rhs_val;
521 return floatSub(sema, lhs_val, rhs_val, ty);
522 }
523}
524
525/// Applies the `-%` operator to comptime-known values.
526/// `lhs_val` and `rhs_val` are both of type `ty`.
527/// `ty` is an int, comptime_int, or vector thereof.
528pub fn subWrap(
529 sema: *Sema,
530 ty: Type,
531 lhs_val: Value,
532 rhs_val: Value,
533) CompileError!Value {
534 const pt = sema.pt;
535 const zcu = pt.zcu;
536 switch (ty.zigTypeTag(zcu)) {
537 .vector => {
538 const elem_ty = ty.childType(zcu);
539 const len = ty.vectorLen(zcu);
540
541 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
542 for (elem_vals, 0..) |*result_elem, elem_idx| {
543 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
544 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
545 result_elem.* = (try subWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
546 }
547 return pt.aggregateValue(ty, elem_vals);
548 },
549 else => return subWrapScalar(sema, ty, lhs_val, rhs_val),
550 }
551}
552fn subWrapScalar(
553 sema: *Sema,
554 ty: Type,
555 lhs_val: Value,
556 rhs_val: Value,
557) CompileError!Value {
558 const pt = sema.pt;
559 const zcu = pt.zcu;
560 switch (ty.zigTypeTag(zcu)) {
561 .int, .comptime_int => {},
562 else => unreachable,
563 }
564 if (lhs_val.isUndef(zcu)) return lhs_val;
565 if (rhs_val.isUndef(zcu)) return rhs_val;
566 const result = try intSubWithOverflow(sema, lhs_val, rhs_val, ty);
567 return result.wrapped_result;
568}
569
570/// Applies the `-|` operator to comptime-known values.
571/// `lhs_val` and `rhs_val` are both of type `ty`.
572/// `ty` is an int, comptime_int, or vector thereof.
573pub fn subSat(
574 sema: *Sema,
575 ty: Type,
576 lhs_val: Value,
577 rhs_val: Value,
578) CompileError!Value {
579 const pt = sema.pt;
580 const zcu = pt.zcu;
581 switch (ty.zigTypeTag(zcu)) {
582 .vector => {
583 const elem_ty = ty.childType(zcu);
584 const len = ty.vectorLen(zcu);
585
586 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
587 for (elem_vals, 0..) |*result_elem, elem_idx| {
588 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
589 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
590 result_elem.* = (try subSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
591 }
592 return pt.aggregateValue(ty, elem_vals);
593 },
594 else => return subSatScalar(sema, ty, lhs_val, rhs_val),
595 }
596}
597fn subSatScalar(
598 sema: *Sema,
599 ty: Type,
600 lhs_val: Value,
601 rhs_val: Value,
602) CompileError!Value {
603 const pt = sema.pt;
604 const zcu = pt.zcu;
605 const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
606 .int => false,
607 .comptime_int => true,
608 else => unreachable,
609 };
610 if (lhs_val.isUndef(zcu)) return lhs_val;
611 if (rhs_val.isUndef(zcu)) return rhs_val;
612 if (is_comptime_int) {
613 const res = try intSub(sema, lhs_val, rhs_val, ty);
614 assert(!res.overflow);
615 return res.val;
616 } else {
617 return intSubSat(sema, lhs_val, rhs_val, ty);
618 }
619}
620
621/// Applies the `*` operator to comptime-known values.
622/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
623/// `ty` is an int, float, comptime_int, comptime_float, or vector.
624pub fn mul(
625 sema: *Sema,
626 block: *Block,
627 ty: Type,
628 lhs_val: Value,
629 rhs_val: Value,
630 src: LazySrcLoc,
631 lhs_src: LazySrcLoc,
632 rhs_src: LazySrcLoc,
633) CompileError!Value {
634 const pt = sema.pt;
635 const zcu = pt.zcu;
636 switch (ty.zigTypeTag(zcu)) {
637 .int, .comptime_int => return mulScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
638 .float, .comptime_float => return mulScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
639 .vector => {
640 const elem_ty = ty.childType(zcu);
641 const len = ty.vectorLen(zcu);
642
643 const is_int = switch (elem_ty.zigTypeTag(zcu)) {
644 .int, .comptime_int => true,
645 .float, .comptime_float => false,
646 else => unreachable,
647 };
648
649 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
650 for (elem_vals, 0..) |*result_elem, elem_idx| {
651 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
652 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
653 result_elem.* = (try mulScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
654 }
655 return pt.aggregateValue(ty, elem_vals);
656 },
657 else => unreachable,
658 }
659}
660fn mulScalar(
661 sema: *Sema,
662 block: *Block,
663 ty: Type,
664 lhs_val: Value,
665 rhs_val: Value,
666 src: LazySrcLoc,
667 lhs_src: LazySrcLoc,
668 rhs_src: LazySrcLoc,
669 is_int: bool,
670 vec_idx: ?usize,
671) CompileError!Value {
672 const pt = sema.pt;
673 const zcu = pt.zcu;
674
675 if (is_int) {
676 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
677 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
678 const res = try intMul(sema, lhs_val, rhs_val, ty);
679 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
680 return res.val;
681 } else {
682 if (lhs_val.isUndef(zcu)) return lhs_val;
683 if (rhs_val.isUndef(zcu)) return rhs_val;
684 return floatMul(sema, lhs_val, rhs_val, ty);
685 }
686}
687
688/// Applies the `*%` operator to comptime-known values.
689/// `lhs_val` and `rhs_val` are both of type `ty`.
690/// `ty` is an int, comptime_int, or vector thereof.
691pub fn mulWrap(
692 sema: *Sema,
693 ty: Type,
694 lhs_val: Value,
695 rhs_val: Value,
696) CompileError!Value {
697 const pt = sema.pt;
698 const zcu = pt.zcu;
699 switch (ty.zigTypeTag(zcu)) {
700 .vector => {
701 const elem_ty = ty.childType(zcu);
702 const len = ty.vectorLen(zcu);
703
704 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
705 for (elem_vals, 0..) |*result_elem, elem_idx| {
706 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
707 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
708 result_elem.* = (try mulWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
709 }
710 return pt.aggregateValue(ty, elem_vals);
711 },
712 else => return mulWrapScalar(sema, ty, lhs_val, rhs_val),
713 }
714}
715fn mulWrapScalar(
716 sema: *Sema,
717 ty: Type,
718 lhs_val: Value,
719 rhs_val: Value,
720) CompileError!Value {
721 const pt = sema.pt;
722 const zcu = pt.zcu;
723 switch (ty.zigTypeTag(zcu)) {
724 .int, .comptime_int => {},
725 else => unreachable,
726 }
727 if (lhs_val.isUndef(zcu)) return lhs_val;
728 if (rhs_val.isUndef(zcu)) return rhs_val;
729 const result = try intMulWithOverflow(sema, lhs_val, rhs_val, ty);
730 return result.wrapped_result;
731}
732
733/// Applies the `*|` operator to comptime-known values.
734/// `lhs_val` and `rhs_val` are both of type `ty`.
735/// `ty` is an int, comptime_int, or vector thereof.
736pub fn mulSat(
737 sema: *Sema,
738 ty: Type,
739 lhs_val: Value,
740 rhs_val: Value,
741) CompileError!Value {
742 const pt = sema.pt;
743 const zcu = pt.zcu;
744 switch (ty.zigTypeTag(zcu)) {
745 .vector => {
746 const elem_ty = ty.childType(zcu);
747 const len = ty.vectorLen(zcu);
748
749 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
750 for (elem_vals, 0..) |*result_elem, elem_idx| {
751 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
752 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
753 result_elem.* = (try mulSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
754 }
755 return pt.aggregateValue(ty, elem_vals);
756 },
757 else => return mulSatScalar(sema, ty, lhs_val, rhs_val),
758 }
759}
760fn mulSatScalar(
761 sema: *Sema,
762 ty: Type,
763 lhs_val: Value,
764 rhs_val: Value,
765) CompileError!Value {
766 const pt = sema.pt;
767 const zcu = pt.zcu;
768 const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
769 .int => false,
770 .comptime_int => true,
771 else => unreachable,
772 };
773 if (lhs_val.isUndef(zcu)) return lhs_val;
774 if (rhs_val.isUndef(zcu)) return rhs_val;
775 if (is_comptime_int) {
776 const res = try intMul(sema, lhs_val, rhs_val, ty);
777 assert(!res.overflow);
778 return res.val;
779 } else {
780 return intMulSat(sema, lhs_val, rhs_val, ty);
781 }
782}
783
784pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact };
785
786/// Applies the `/` operator to comptime-known values.
787/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
788/// `ty` is an int, float, comptime_int, comptime_float, or vector.
789pub fn div(
790 sema: *Sema,
791 block: *Block,
792 ty: Type,
793 lhs_val: Value,
794 rhs_val: Value,
795 src: LazySrcLoc,
796 lhs_src: LazySrcLoc,
797 rhs_src: LazySrcLoc,
798 op: DivOp,
799) CompileError!Value {
800 const pt = sema.pt;
801 const zcu = pt.zcu;
802 switch (ty.zigTypeTag(zcu)) {
803 .int, .comptime_int => return divScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, true, null),
804 .float, .comptime_float => return divScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, false, null),
805 .vector => {
806 const elem_ty = ty.childType(zcu);
807 const len = ty.vectorLen(zcu);
808
809 const is_int = switch (elem_ty.zigTypeTag(zcu)) {
810 .int, .comptime_int => true,
811 .float, .comptime_float => false,
812 else => unreachable,
813 };
814
815 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
816 for (elem_vals, 0..) |*result_elem, elem_idx| {
817 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
818 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
819 result_elem.* = (try divScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, is_int, elem_idx)).toIntern();
820 }
821 return pt.aggregateValue(ty, elem_vals);
822 },
823 else => unreachable,
824 }
825}
826fn divScalar(
827 sema: *Sema,
828 block: *Block,
829 ty: Type,
830 lhs_val: Value,
831 rhs_val: Value,
832 src: LazySrcLoc,
833 lhs_src: LazySrcLoc,
834 rhs_src: LazySrcLoc,
835 op: DivOp,
836 is_int: bool,
837 vec_idx: ?usize,
838) CompileError!Value {
839 const pt = sema.pt;
840 const zcu = pt.zcu;
841
842 if (is_int) {
843 if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
844
845 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
846 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
847
848 switch (op) {
849 .div, .div_trunc => {
850 const res = try intDivTrunc(sema, lhs_val, rhs_val, ty);
851 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
852 return res.val;
853 },
854 .div_floor => {
855 const res = try intDivFloor(sema, lhs_val, rhs_val, ty);
856 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
857 return res.val;
858 },
859 .div_ceil => {
860 const res = try intDivCeil(sema, lhs_val, rhs_val, ty);
861 if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
862 return res.val;
863 },
864 .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
865 .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
866 .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
867 .success => |val| return val,
868 },
869 }
870 } else {
871 const allow_div_zero = switch (op) {
872 .div, .div_trunc, .div_floor, .div_ceil => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
873 .div_exact => false,
874 };
875 if (!allow_div_zero) {
876 if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
877 }
878
879 const can_exhibit_ib = !allow_div_zero or op == .div_exact;
880 if (can_exhibit_ib) {
881 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
882 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
883 } else {
884 if (lhs_val.isUndef(zcu)) return lhs_val;
885 if (rhs_val.isUndef(zcu)) return rhs_val;
886 }
887
888 switch (op) {
889 .div => return floatDiv(sema, lhs_val, rhs_val, ty),
890 .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
891 .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
892 .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty),
893 .div_exact => {
894 if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
895 return sema.fail(block, src, "exact division produced remainder", .{});
896 }
897 return floatDivTrunc(sema, lhs_val, rhs_val, ty);
898 },
899 }
900 }
901}
902
903pub const ModRemOp = enum { mod, rem };
904
905/// Applies `@mod` or `@rem` to comptime-known values.
906/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
907/// `ty` is an int, float, comptime_int, comptime_float, or vector.
908pub fn modRem(
909 sema: *Sema,
910 block: *Block,
911 ty: Type,
912 lhs_val: Value,
913 rhs_val: Value,
914 lhs_src: LazySrcLoc,
915 rhs_src: LazySrcLoc,
916 op: ModRemOp,
917) CompileError!Value {
918 const pt = sema.pt;
919 const zcu = pt.zcu;
920 switch (ty.zigTypeTag(zcu)) {
921 .vector => {
922 const elem_ty = ty.childType(zcu);
923 const len = ty.vectorLen(zcu);
924
925 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
926 for (elem_vals, 0..) |*result_elem, elem_idx| {
927 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
928 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
929 result_elem.* = (try modRemScalar(sema, block, elem_ty, lhs_elem, rhs_elem, lhs_src, rhs_src, op, elem_idx)).toIntern();
930 }
931 return pt.aggregateValue(ty, elem_vals);
932 },
933 else => return modRemScalar(sema, block, ty, lhs_val, rhs_val, lhs_src, rhs_src, op, null),
934 }
935}
936fn modRemScalar(
937 sema: *Sema,
938 block: *Block,
939 ty: Type,
940 lhs_val: Value,
941 rhs_val: Value,
942 lhs_src: LazySrcLoc,
943 rhs_src: LazySrcLoc,
944 op: ModRemOp,
945 vec_idx: ?usize,
946) CompileError!Value {
947 const pt = sema.pt;
948 const zcu = pt.zcu;
949 const is_int = switch (ty.zigTypeTag(zcu)) {
950 .int, .comptime_int => true,
951 .float, .comptime_float => false,
952 else => unreachable,
953 };
954
955 const allow_div_zero = !is_int and block.float_mode == .strict;
956 if (allow_div_zero) {
957 if (lhs_val.isUndef(zcu)) return lhs_val;
958 if (rhs_val.isUndef(zcu)) return rhs_val;
959 } else {
960 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
961 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
962 if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
963 }
964
965 if (is_int) {
966 switch (op) {
967 .mod => return intMod(sema, lhs_val, rhs_val, ty),
968 .rem => return intRem(sema, lhs_val, rhs_val, ty),
969 }
970 } else {
971 switch (op) {
972 .mod => return floatMod(sema, lhs_val, rhs_val, ty),
973 .rem => return floatRem(sema, lhs_val, rhs_val, ty),
974 }
975 }
976}
977
978pub const ShlOp = enum { shl, shl_sat, shl_exact };
979
980/// Applies the `<<` operator to comptime-known values.
981/// `lhs_ty` is an int, comptime_int, or vector thereof.
982/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
983pub fn shl(
984 sema: *Sema,
985 block: *Block,
986 lhs_ty: Type,
987 lhs_val: Value,
988 rhs_val: Value,
989 src: LazySrcLoc,
990 lhs_src: LazySrcLoc,
991 rhs_src: LazySrcLoc,
992 op: ShlOp,
993) CompileError!Value {
994 const pt = sema.pt;
995 const zcu = pt.zcu;
996 switch (lhs_ty.zigTypeTag(zcu)) {
997 .int, .comptime_int => return shlScalar(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, null),
998 .vector => {
999 const lhs_elem_ty = lhs_ty.childType(zcu);
1000 const len = lhs_ty.vectorLen(zcu);
1001
1002 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1003 for (elem_vals, 0..) |*result_elem, elem_idx| {
1004 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1005 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1006 result_elem.* = (try shlScalar(sema, block, lhs_elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, elem_idx)).toIntern();
1007 }
1008 return pt.aggregateValue(lhs_ty, elem_vals);
1009 },
1010 else => unreachable,
1011 }
1012}
1013/// `lhs_ty` is an int, comptime_int, or vector thereof.
1014/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
1015pub fn shlWithOverflow(
1016 sema: *Sema,
1017 block: *Block,
1018 lhs_ty: Type,
1019 lhs_val: Value,
1020 rhs_val: Value,
1021 lhs_src: LazySrcLoc,
1022 rhs_src: LazySrcLoc,
1023) CompileError!Value.OverflowArithmeticResult {
1024 const pt = sema.pt;
1025 const zcu = pt.zcu;
1026 switch (lhs_ty.zigTypeTag(zcu)) {
1027 .int, .comptime_int => return shlWithOverflowScalar(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src, null),
1028 .vector => {
1029 const lhs_elem_ty = lhs_ty.childType(zcu);
1030 const len = lhs_ty.vectorLen(zcu);
1031
1032 const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
1033 const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
1034 for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
1035 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1036 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1037 const elem_result = try shlWithOverflowScalar(sema, block, lhs_elem_ty, lhs_elem, rhs_elem, lhs_src, rhs_src, elem_idx);
1038 ob.* = elem_result.overflow_bit.toIntern();
1039 wr.* = elem_result.wrapped_result.toIntern();
1040 }
1041 return .{
1042 .overflow_bit = try pt.aggregateValue(try pt.vectorType(.{
1043 .len = @intCast(overflow_bits.len),
1044 .child = .u1_type,
1045 }), overflow_bits),
1046 .wrapped_result = try pt.aggregateValue(lhs_ty, wrapped_results),
1047 };
1048 },
1049 else => unreachable,
1050 }
1051}
1052
1053fn shlScalar(
1054 sema: *Sema,
1055 block: *Block,
1056 lhs_ty: Type,
1057 lhs_val: Value,
1058 rhs_val: Value,
1059 src: LazySrcLoc,
1060 lhs_src: LazySrcLoc,
1061 rhs_src: LazySrcLoc,
1062 op: ShlOp,
1063 vec_idx: ?usize,
1064) CompileError!Value {
1065 const pt = sema.pt;
1066 const zcu = pt.zcu;
1067
1068 switch (op) {
1069 .shl, .shl_exact => {
1070 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1071 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1072 },
1073 .shl_sat => {
1074 if (lhs_val.isUndef(zcu)) return lhs_val;
1075 if (rhs_val.isUndef(zcu)) return rhs_val;
1076 },
1077 }
1078 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
1079 .gt => {},
1080 .eq => return lhs_val,
1081 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1082 }
1083 switch (lhs_ty.zigTypeTag(zcu)) {
1084 .int => switch (op) {
1085 .shl => return intShl(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, vec_idx),
1086 .shl_sat => return intShlSat(sema, lhs_ty, lhs_val, rhs_val),
1087 .shl_exact => {
1088 const shifted = try intShlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, false, vec_idx);
1089 if (shifted.overflow) {
1090 return sema.failWithIntegerOverflow(block, src, lhs_ty, shifted.val, vec_idx);
1091 }
1092 return shifted.val;
1093 },
1094 },
1095 .comptime_int => return comptimeIntShl(sema, block, lhs_val, rhs_val, rhs_src, vec_idx),
1096 else => unreachable,
1097 }
1098}
1099fn shlWithOverflowScalar(
1100 sema: *Sema,
1101 block: *Block,
1102 lhs_ty: Type,
1103 lhs_val: Value,
1104 rhs_val: Value,
1105 lhs_src: LazySrcLoc,
1106 rhs_src: LazySrcLoc,
1107 vec_idx: ?usize,
1108) CompileError!Value.OverflowArithmeticResult {
1109 const pt = sema.pt;
1110 const zcu = pt.zcu;
1111
1112 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1113 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1114
1115 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
1116 .gt => {},
1117 .eq => return .{ .overflow_bit = .zero_u1, .wrapped_result = lhs_val },
1118 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1119 }
1120 switch (lhs_ty.zigTypeTag(zcu)) {
1121 .int => {
1122 const result = try intShlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, true, vec_idx);
1123 return .{
1124 .overflow_bit = if (result.overflow) .one_u1 else .zero_u1,
1125 .wrapped_result = result.val,
1126 };
1127 },
1128 .comptime_int => return .{
1129 .overflow_bit = .zero_u1,
1130 .wrapped_result = try comptimeIntShl(sema, block, lhs_val, rhs_val, rhs_src, vec_idx),
1131 },
1132 else => unreachable,
1133 }
1134}
1135
1136pub const ShrOp = enum { shr, shr_exact };
1137
1138/// Applies the `>>` operator to comptime-known values.
1139/// `lhs_ty` is an int, comptime_int, or vector thereof.
1140/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
1141pub fn shr(
1142 sema: *Sema,
1143 block: *Block,
1144 lhs_ty: Type,
1145 rhs_ty: Type,
1146 lhs_val: Value,
1147 rhs_val: Value,
1148 src: LazySrcLoc,
1149 lhs_src: LazySrcLoc,
1150 rhs_src: LazySrcLoc,
1151 op: ShrOp,
1152) CompileError!Value {
1153 const pt = sema.pt;
1154 const zcu = pt.zcu;
1155
1156 switch (lhs_ty.zigTypeTag(zcu)) {
1157 .int, .comptime_int => return shrScalar(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, null),
1158 .vector => {
1159 const lhs_elem_ty = lhs_ty.childType(zcu);
1160 const rhs_elem_ty = rhs_ty.childType(zcu);
1161 const len = lhs_ty.vectorLen(zcu);
1162
1163 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1164 for (elem_vals, 0..) |*result_elem, elem_idx| {
1165 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1166 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1167 result_elem.* = (try shrScalar(sema, block, lhs_elem_ty, rhs_elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, elem_idx)).toIntern();
1168 }
1169 return pt.aggregateValue(lhs_ty, elem_vals);
1170 },
1171 else => unreachable,
1172 }
1173}
1174
1175fn shrScalar(
1176 sema: *Sema,
1177 block: *Block,
1178 lhs_ty: Type,
1179 rhs_ty: Type,
1180 lhs_val: Value,
1181 rhs_val: Value,
1182 src: LazySrcLoc,
1183 lhs_src: LazySrcLoc,
1184 rhs_src: LazySrcLoc,
1185 op: ShrOp,
1186 vec_idx: ?usize,
1187) CompileError!Value {
1188 const pt = sema.pt;
1189 const zcu = pt.zcu;
1190
1191 if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1192 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1193
1194 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
1195 .gt => {},
1196 .eq => return lhs_val,
1197 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1198 }
1199 return intShr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, rhs_src, op, vec_idx);
1200}
1201
1202/// Applies `@truncate` to comptime-known values.
1203/// `ty` is an int, comptime_int, or vector thereof.
1204/// `val` is of type `ty`.
1205/// The returned value is of type `dest_ty`. The caller guarantees that the
1206/// truncated value fits into `dest_ty`.
1207/// If `ty` is a vector, `dest_ty` has to also be a vector of the same length.
1208pub fn truncate(
1209 sema: *Sema,
1210 val: Value,
1211 ty: Type,
1212 dest_ty: Type,
1213 dest_signedness: std.lang.Signedness,
1214 dest_bits: u16,
1215) CompileError!Value {
1216 const pt = sema.pt;
1217 const zcu = pt.zcu;
1218 if (val.isUndef(zcu)) return pt.undefValue(dest_ty);
1219 switch (ty.zigTypeTag(zcu)) {
1220 .int, .comptime_int => return intTruncate(sema, val, dest_ty, dest_signedness, dest_bits),
1221 .vector => {
1222 const dest_elem_ty = dest_ty.childType(zcu);
1223 const len = ty.vectorLen(zcu);
1224
1225 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1226 for (elem_vals, 0..) |*result_elem, elem_idx| {
1227 const elem_val = try val.elemValue(pt, elem_idx);
1228 result_elem.* = if (elem_val.isUndef(zcu))
1229 (try pt.undefValue(dest_elem_ty)).toIntern()
1230 else
1231 (try intTruncate(
1232 sema,
1233 elem_val,
1234 dest_elem_ty,
1235 dest_signedness,
1236 dest_bits,
1237 )).toIntern();
1238 }
1239 return pt.aggregateValue(dest_ty, elem_vals);
1240 },
1241 else => unreachable,
1242 }
1243}
1244
1245/// Applies the `~` operator to a comptime-known value.
1246/// `val` is of type `ty`.
1247/// `ty` is a bool, int, comptime_int, or vector thereof.
1248pub fn bitwiseNot(sema: *Sema, ty: Type, val: Value) CompileError!Value {
1249 const pt = sema.pt;
1250 const zcu = pt.zcu;
1251 if (val.isUndef(zcu)) return val;
1252 switch (ty.zigTypeTag(zcu)) {
1253 .bool, .int, .comptime_int => return intBitwiseNot(sema, val, ty),
1254 .vector => {
1255 const elem_ty = ty.childType(zcu);
1256 switch (elem_ty.zigTypeTag(zcu)) {
1257 .bool, .int, .comptime_int => {},
1258 else => unreachable,
1259 }
1260 const len = ty.vectorLen(zcu);
1261
1262 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1263 for (elem_vals, 0..) |*result_elem, elem_idx| {
1264 const elem_val = try val.elemValue(pt, elem_idx);
1265 result_elem.* = if (elem_val.isUndef(zcu))
1266 elem_val.toIntern()
1267 else
1268 (try intBitwiseNot(sema, elem_val, elem_ty)).toIntern();
1269 }
1270 return pt.aggregateValue(ty, elem_vals);
1271 },
1272 else => unreachable,
1273 }
1274}
1275
1276pub const BitwiseBinOp = enum { @"and", nand, @"or", xor };
1277
1278/// Applies a binary bitwise operator to comptime-known values.
1279/// `lhs_val` and `rhs_val` are both of type `ty`.
1280/// `ty` is a bool, int, comptime_int, or vector thereof.
1281pub fn bitwiseBin(
1282 sema: *Sema,
1283 ty: Type,
1284 lhs_val: Value,
1285 rhs_val: Value,
1286 op: BitwiseBinOp,
1287) CompileError!Value {
1288 const pt = sema.pt;
1289 const zcu = pt.zcu;
1290 switch (ty.zigTypeTag(zcu)) {
1291 .vector => {
1292 const elem_ty = ty.childType(zcu);
1293 switch (elem_ty.zigTypeTag(zcu)) {
1294 .bool, .int, .comptime_int => {},
1295 else => unreachable,
1296 }
1297 const len = ty.vectorLen(zcu);
1298
1299 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1300 for (elem_vals, 0..) |*result_elem, elem_idx| {
1301 const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1302 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1303 result_elem.* = (try bitwiseBinScalar(sema, elem_ty, lhs_elem, rhs_elem, op)).toIntern();
1304 }
1305 return pt.aggregateValue(ty, elem_vals);
1306 },
1307 .bool, .int, .comptime_int => return bitwiseBinScalar(sema, ty, lhs_val, rhs_val, op),
1308 else => unreachable,
1309 }
1310}
1311fn bitwiseBinScalar(
1312 sema: *Sema,
1313 ty: Type,
1314 lhs_val: Value,
1315 rhs_val: Value,
1316 op: BitwiseBinOp,
1317) CompileError!Value {
1318 const pt = sema.pt;
1319 const zcu = pt.zcu;
1320 // Special case: the method used below doesn't make sense for xor.
1321 if (op == .xor and (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu))) return pt.undefValue(ty);
1322 // If one operand is defined, we turn the other into `0xAA` so the bitwise op can
1323 // still zero out some bits.
1324 // TODO: ideally we'd still like tracking for the undef bits. Related: #19634.
1325 const def_lhs: Value, const def_rhs: Value = make_defined: {
1326 const lhs_undef = lhs_val.isUndef(zcu);
1327 const rhs_undef = rhs_val.isUndef(zcu);
1328 break :make_defined switch ((@as(u2, @intFromBool(lhs_undef)) << 1) | @intFromBool(rhs_undef)) {
1329 0b00 => .{ lhs_val, rhs_val },
1330 0b01 => .{ lhs_val, try intValueAa(sema, ty) },
1331 0b10 => .{ try intValueAa(sema, ty), rhs_val },
1332 0b11 => return pt.undefValue(ty),
1333 };
1334 };
1335 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
1336 // zig fmt: off
1337 switch (op) {
1338 .@"and" => return intBitwiseAnd(sema, def_lhs, def_rhs, ty),
1339 .nand => return intBitwiseNand(sema, def_lhs, def_rhs, ty),
1340 .@"or" => return intBitwiseOr(sema, def_lhs, def_rhs, ty),
1341 .xor => return intBitwiseXor(sema, def_lhs, def_rhs, ty),
1342 }
1343 // zig fmt: on
1344}
1345
1346/// Applies `@bitReverse` to a comptime-known value.
1347/// `val` is of type `ty`.
1348/// `ty` is an int or a vector thereof.
1349pub fn bitReverse(sema: *Sema, val: Value, ty: Type) CompileError!Value {
1350 const pt = sema.pt;
1351 const zcu = pt.zcu;
1352 if (val.isUndef(zcu)) return val;
1353 switch (ty.zigTypeTag(zcu)) {
1354 .int => return intBitReverse(sema, val, ty),
1355 .vector => {
1356 const elem_ty = ty.childType(zcu);
1357 assert(elem_ty.isInt(zcu));
1358 const len = ty.vectorLen(zcu);
1359
1360 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1361 for (elem_vals, 0..) |*result_elem, elem_idx| {
1362 const elem_val = try val.elemValue(pt, elem_idx);
1363 result_elem.* = if (elem_val.isUndef(zcu))
1364 elem_val.toIntern()
1365 else
1366 (try intBitReverse(sema, elem_val, elem_ty)).toIntern();
1367 }
1368 return pt.aggregateValue(ty, elem_vals);
1369 },
1370 else => unreachable,
1371 }
1372}
1373
1374/// Applies `@byteSwap` to a comptime-known value.
1375/// `val` is of type `ty`.
1376/// `ty` is an int or a vector thereof.
1377/// The bit width of the scalar int type of `ty` has to be a multiple of 8.
1378pub fn byteSwap(sema: *Sema, val: Value, ty: Type) CompileError!Value {
1379 const pt = sema.pt;
1380 const zcu = pt.zcu;
1381 if (val.isUndef(zcu)) return val;
1382 switch (ty.zigTypeTag(zcu)) {
1383 .int => return intByteSwap(sema, val, ty),
1384 .vector => {
1385 const elem_ty = ty.childType(zcu);
1386 assert(elem_ty.isInt(zcu));
1387 const len = ty.vectorLen(zcu);
1388
1389 const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1390 for (elem_vals, 0..) |*result_elem, elem_idx| {
1391 const elem_val = try val.elemValue(pt, elem_idx);
1392 result_elem.* = if (elem_val.isUndef(zcu))
1393 elem_val.toIntern()
1394 else
1395 (try intByteSwap(sema, elem_val, elem_ty)).toIntern();
1396 }
1397 return pt.aggregateValue(ty, elem_vals);
1398 },
1399 else => unreachable,
1400 }
1401}
1402
1403/// If the value overflowed the type, returns a comptime_int instead.
1404/// Only supports scalars.
1405fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1406 switch (ty.toIntern()) {
1407 .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntAdd(sema, lhs, rhs) },
1408 else => {
1409 const res = try intAddWithOverflowInner(sema, lhs, rhs, ty);
1410 if (res.overflow) return .{ .overflow = true, .val = try comptimeIntAdd(sema, lhs, rhs) };
1411 return .{ .overflow = false, .val = res.wrapped_result };
1412 },
1413 }
1414}
1415/// Add two integers, returning a `comptime_int` regardless of the input types.
1416fn comptimeIntAdd(sema: *Sema, lhs: Value, rhs: Value) !Value {
1417 const pt = sema.pt;
1418 const zcu = pt.zcu;
1419
1420 // Try the operation without resorting to BigInt first.
1421 if (lhs.getUnsignedInt(zcu)) |lhs_val| {
1422 if (rhs.getUnsignedInt(zcu)) |rhs_val| {
1423 const result, const overflow = @addWithOverflow(lhs_val, rhs_val);
1424
1425 if (overflow == 0) {
1426 return pt.intValue(.comptime_int, result);
1427 }
1428 // It would overflow, fall back to BigInt.
1429 }
1430 }
1431
1432 var lhs_space: Value.BigIntSpace = undefined;
1433 var rhs_space: Value.BigIntSpace = undefined;
1434 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1435 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1436 const limbs = try sema.arena.alloc(
1437 std.math.big.Limb,
1438 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1439 );
1440 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1441 result_bigint.add(lhs_bigint, rhs_bigint);
1442 return pt.intValue_big(.comptime_int, result_bigint.toConst());
1443}
1444fn intAddWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1445 switch (ty.toIntern()) {
1446 .comptime_int_type => return .{
1447 .overflow = false,
1448 .wrapped_result = try comptimeIntAdd(sema, lhs, rhs),
1449 },
1450 else => return intAddWithOverflowInner(sema, lhs, rhs, ty),
1451 }
1452}
1453/// Like `intAddWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1454fn intAddWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1455 assert(ty.toIntern() != .comptime_int_type);
1456 const pt = sema.pt;
1457 const zcu = pt.zcu;
1458 const info = ty.intInfo(zcu);
1459 var lhs_space: Value.BigIntSpace = undefined;
1460 var rhs_space: Value.BigIntSpace = undefined;
1461 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1462 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1463 const limbs = try sema.arena.alloc(
1464 std.math.big.Limb,
1465 std.math.big.int.calcTwosCompLimbCount(info.bits),
1466 );
1467 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1468 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1469 return .{
1470 .overflow = overflowed,
1471 .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1472 };
1473}
1474fn intAddSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1475 const pt = sema.pt;
1476 const zcu = pt.zcu;
1477 const info = ty.intInfo(zcu);
1478 var lhs_space: Value.BigIntSpace = undefined;
1479 var rhs_space: Value.BigIntSpace = undefined;
1480 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1481 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1482 const limbs = try sema.arena.alloc(
1483 std.math.big.Limb,
1484 std.math.big.int.calcTwosCompLimbCount(info.bits),
1485 );
1486 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1487 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1488 return pt.intValue_big(ty, result_bigint.toConst());
1489}
1490
1491/// If the value overflowed the type, returns a comptime_int instead.
1492/// Only supports scalars.
1493fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1494 switch (ty.toIntern()) {
1495 .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntSub(sema, lhs, rhs) },
1496 else => {
1497 const res = try intSubWithOverflowInner(sema, lhs, rhs, ty);
1498 if (res.overflow) return .{ .overflow = true, .val = try comptimeIntSub(sema, lhs, rhs) };
1499 return .{ .overflow = false, .val = res.wrapped_result };
1500 },
1501 }
1502}
1503/// Subtract two integers, returning a `comptime_int` regardless of the input types.
1504fn comptimeIntSub(sema: *Sema, lhs: Value, rhs: Value) !Value {
1505 const pt = sema.pt;
1506 const zcu = pt.zcu;
1507
1508 // Try the operation without resorting to BigInt first.
1509 if (lhs.getUnsignedInt(zcu)) |lhs_val| {
1510 if (rhs.getUnsignedInt(zcu)) |rhs_val| {
1511 const result, const overflow = @subWithOverflow(lhs_val, rhs_val);
1512
1513 if (overflow == 0) {
1514 return pt.intValue(.comptime_int, result);
1515 }
1516 // It would overflow, fall back to BigInt.
1517 }
1518 }
1519
1520 var lhs_space: Value.BigIntSpace = undefined;
1521 var rhs_space: Value.BigIntSpace = undefined;
1522 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1523 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1524 const limbs = try sema.arena.alloc(
1525 std.math.big.Limb,
1526 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1527 );
1528 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1529 result_bigint.sub(lhs_bigint, rhs_bigint);
1530 return pt.intValue_big(.comptime_int, result_bigint.toConst());
1531}
1532fn intSubWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1533 switch (ty.toIntern()) {
1534 .comptime_int_type => return .{
1535 .overflow = false,
1536 .wrapped_result = try comptimeIntSub(sema, lhs, rhs),
1537 },
1538 else => return intSubWithOverflowInner(sema, lhs, rhs, ty),
1539 }
1540}
1541/// Like `intSubWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1542fn intSubWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1543 assert(ty.toIntern() != .comptime_int_type);
1544 const pt = sema.pt;
1545 const zcu = pt.zcu;
1546 const info = ty.intInfo(zcu);
1547 var lhs_space: Value.BigIntSpace = undefined;
1548 var rhs_space: Value.BigIntSpace = undefined;
1549 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1550 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1551 const limbs = try sema.arena.alloc(
1552 std.math.big.Limb,
1553 std.math.big.int.calcTwosCompLimbCount(info.bits),
1554 );
1555 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1556 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1557 return .{
1558 .overflow = overflowed,
1559 .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1560 };
1561}
1562fn intSubSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1563 const pt = sema.pt;
1564 const zcu = pt.zcu;
1565 const info = ty.intInfo(zcu);
1566 var lhs_space: Value.BigIntSpace = undefined;
1567 var rhs_space: Value.BigIntSpace = undefined;
1568 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1569 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1570 const limbs = try sema.arena.alloc(
1571 std.math.big.Limb,
1572 std.math.big.int.calcTwosCompLimbCount(info.bits),
1573 );
1574 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1575 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1576 return pt.intValue_big(ty, result_bigint.toConst());
1577}
1578
1579/// If the value overflowed the type, returns a comptime_int instead.
1580/// Only supports scalars.
1581fn intMul(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1582 switch (ty.toIntern()) {
1583 .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntMul(sema, lhs, rhs) },
1584 else => {
1585 const res = try intMulWithOverflowInner(sema, lhs, rhs, ty);
1586 if (res.overflow) return .{ .overflow = true, .val = try comptimeIntMul(sema, lhs, rhs) };
1587 return .{ .overflow = false, .val = res.wrapped_result };
1588 },
1589 }
1590}
1591/// Multiply two integers, returning a `comptime_int` regardless of the input types.
1592fn comptimeIntMul(sema: *Sema, lhs: Value, rhs: Value) !Value {
1593 const pt = sema.pt;
1594 const zcu = pt.zcu;
1595
1596 // Try the operation without resorting to BigInt first.
1597 if (lhs.getUnsignedInt(zcu)) |lhs_val| {
1598 if (rhs.getUnsignedInt(zcu)) |rhs_val| {
1599 const result, const overflow = @mulWithOverflow(lhs_val, rhs_val);
1600
1601 if (overflow == 0) {
1602 return pt.intValue(.comptime_int, result);
1603 }
1604 // It would overflow, fall back to BigInt.
1605 }
1606 }
1607
1608 var lhs_space: Value.BigIntSpace = undefined;
1609 var rhs_space: Value.BigIntSpace = undefined;
1610 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1611 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1612 const limbs = try sema.arena.alloc(
1613 std.math.big.Limb,
1614 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1615 );
1616 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1617 const limbs_buffer = try sema.arena.alloc(
1618 std.math.big.Limb,
1619 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1620 );
1621 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, sema.arena);
1622 return pt.intValue_big(.comptime_int, result_bigint.toConst());
1623}
1624fn intMulWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1625 switch (ty.toIntern()) {
1626 .comptime_int_type => return .{
1627 .overflow = false,
1628 .wrapped_result = try comptimeIntMul(sema, lhs, rhs),
1629 },
1630 else => return intMulWithOverflowInner(sema, lhs, rhs, ty),
1631 }
1632}
1633/// Like `intMulWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1634fn intMulWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResultInt {
1635 const pt = sema.pt;
1636 const zcu = pt.zcu;
1637 const info = ty.intInfo(zcu);
1638 var lhs_space: Value.BigIntSpace = undefined;
1639 var rhs_space: Value.BigIntSpace = undefined;
1640 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1641 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1642 const limbs = try sema.arena.alloc(
1643 std.math.big.Limb,
1644 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1645 );
1646 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1647 result_bigint.mulNoAlias(lhs_bigint, rhs_bigint, sema.arena);
1648 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
1649 if (overflowed) result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1650 return .{
1651 .overflow = overflowed,
1652 .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1653 };
1654}
1655fn intMulSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1656 const pt = sema.pt;
1657 const zcu = pt.zcu;
1658 const info = ty.intInfo(zcu);
1659 var lhs_space: Value.BigIntSpace = undefined;
1660 var rhs_space: Value.BigIntSpace = undefined;
1661 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1662 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1663 const limbs = try sema.arena.alloc(
1664 std.math.big.Limb,
1665 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1666 );
1667 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1668 result_bigint.mulNoAlias(lhs_bigint, rhs_bigint, sema.arena);
1669 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
1670 return pt.intValue_big(ty, result_bigint.toConst());
1671}
1672fn intDivTrunc(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1673 const result = intDivTruncInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1674 error.Overflow => {
1675 const result = intDivTruncInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1676 error.Overflow => unreachable,
1677 else => |e| return e,
1678 };
1679 return .{ .overflow = true, .val = result };
1680 },
1681 else => |e| return e,
1682 };
1683 return .{ .overflow = false, .val = result };
1684}
1685fn intDivTruncInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1686 const pt = sema.pt;
1687 const zcu = pt.zcu;
1688 var lhs_space: Value.BigIntSpace = undefined;
1689 var rhs_space: Value.BigIntSpace = undefined;
1690 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1691 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1692 const limbs_q = try sema.arena.alloc(
1693 std.math.big.Limb,
1694 lhs_bigint.limbs.len,
1695 );
1696 const limbs_r = try sema.arena.alloc(
1697 std.math.big.Limb,
1698 rhs_bigint.limbs.len,
1699 );
1700 const limbs_buf = try sema.arena.alloc(
1701 std.math.big.Limb,
1702 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1703 );
1704 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1705 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1706 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1707 if (ty.toIntern() != .comptime_int_type) {
1708 const info = ty.intInfo(zcu);
1709 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1710 return error.Overflow;
1711 }
1712 }
1713 return pt.intValue_big(ty, result_q.toConst());
1714}
1715fn intDivExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !union(enum) {
1716 remainder,
1717 overflow: Value,
1718 success: Value,
1719} {
1720 const pt = sema.pt;
1721 const zcu = pt.zcu;
1722 var lhs_space: Value.BigIntSpace = undefined;
1723 var rhs_space: Value.BigIntSpace = undefined;
1724 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1725 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1726 const limbs_q = try sema.arena.alloc(
1727 std.math.big.Limb,
1728 lhs_bigint.limbs.len,
1729 );
1730 const limbs_r = try sema.arena.alloc(
1731 std.math.big.Limb,
1732 rhs_bigint.limbs.len,
1733 );
1734 const limbs_buf = try sema.arena.alloc(
1735 std.math.big.Limb,
1736 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1737 );
1738 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1739 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1740 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1741 if (!result_r.toConst().eqlZero()) {
1742 return .remainder;
1743 }
1744 if (ty.toIntern() != .comptime_int_type) {
1745 const info = ty.intInfo(zcu);
1746 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1747 return .{ .overflow = try pt.intValue_big(.comptime_int, result_q.toConst()) };
1748 }
1749 }
1750 return .{ .success = try pt.intValue_big(ty, result_q.toConst()) };
1751}
1752fn intDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1753 const result = intDivFloorInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1754 error.Overflow => {
1755 const result = intDivFloorInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1756 error.Overflow => unreachable,
1757 else => |e| return e,
1758 };
1759 return .{ .overflow = true, .val = result };
1760 },
1761 else => |e| return e,
1762 };
1763 return .{ .overflow = false, .val = result };
1764}
1765fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1766 const pt = sema.pt;
1767 const zcu = pt.zcu;
1768 var lhs_space: Value.BigIntSpace = undefined;
1769 var rhs_space: Value.BigIntSpace = undefined;
1770 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1771 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1772 const limbs_q = try sema.arena.alloc(
1773 std.math.big.Limb,
1774 lhs_bigint.limbs.len,
1775 );
1776 const limbs_r = try sema.arena.alloc(
1777 std.math.big.Limb,
1778 rhs_bigint.limbs.len,
1779 );
1780 const limbs_buf = try sema.arena.alloc(
1781 std.math.big.Limb,
1782 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1783 );
1784 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1785 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1786 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1787 if (ty.toIntern() != .comptime_int_type) {
1788 const info = ty.intInfo(zcu);
1789 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1790 return error.Overflow;
1791 }
1792 }
1793 return pt.intValue_big(ty, result_q.toConst());
1794}
1795fn intDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1796 const result = intDivCeilInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1797 error.Overflow => {
1798 const result = intDivCeilInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1799 error.Overflow => unreachable,
1800 else => |e| return e,
1801 };
1802 return .{ .overflow = true, .val = result };
1803 },
1804 else => |e| return e,
1805 };
1806 return .{ .overflow = false, .val = result };
1807}
1808fn intDivCeilInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1809 const pt = sema.pt;
1810 const zcu = pt.zcu;
1811 var lhs_space: Value.BigIntSpace = undefined;
1812 var rhs_space: Value.BigIntSpace = undefined;
1813 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1814 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1815 const limbs_q = try sema.arena.alloc(
1816 std.math.big.Limb,
1817 lhs_bigint.limbs.len,
1818 );
1819 const limbs_r = try sema.arena.alloc(
1820 std.math.big.Limb,
1821 rhs_bigint.limbs.len,
1822 );
1823 const limbs_buf = try sema.arena.alloc(
1824 std.math.big.Limb,
1825 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1826 );
1827 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1828 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1829 result_q.divCeil(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1830 if (ty.toIntern() != .comptime_int_type) {
1831 const info = ty.intInfo(zcu);
1832 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1833 return error.Overflow;
1834 }
1835 }
1836 return pt.intValue_big(ty, result_q.toConst());
1837}
1838fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1839 const pt = sema.pt;
1840 const zcu = pt.zcu;
1841 var lhs_space: Value.BigIntSpace = undefined;
1842 var rhs_space: Value.BigIntSpace = undefined;
1843 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1844 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1845 const limbs_q = try sema.arena.alloc(
1846 std.math.big.Limb,
1847 lhs_bigint.limbs.len,
1848 );
1849 const limbs_r = try sema.arena.alloc(
1850 std.math.big.Limb,
1851 rhs_bigint.limbs.len,
1852 );
1853 const limbs_buf = try sema.arena.alloc(
1854 std.math.big.Limb,
1855 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1856 );
1857 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1858 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1859 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1860 return pt.intValue_big(ty, result_r.toConst());
1861}
1862fn intRem(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1863 const pt = sema.pt;
1864 const zcu = pt.zcu;
1865 var lhs_space: Value.BigIntSpace = undefined;
1866 var rhs_space: Value.BigIntSpace = undefined;
1867 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1868 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1869 const limbs_q = try sema.arena.alloc(
1870 std.math.big.Limb,
1871 lhs_bigint.limbs.len,
1872 );
1873 const limbs_r = try sema.arena.alloc(
1874 std.math.big.Limb,
1875 rhs_bigint.limbs.len,
1876 );
1877 const limbs_buf = try sema.arena.alloc(
1878 std.math.big.Limb,
1879 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1880 );
1881 var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1882 var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1883 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1884 return pt.intValue_big(ty, result_r.toConst());
1885}
1886
1887fn intTruncate(
1888 sema: *Sema,
1889 val: Value,
1890 dest_ty: Type,
1891 dest_signedness: std.lang.Signedness,
1892 dest_bits: u16,
1893) !Value {
1894 const pt = sema.pt;
1895 const zcu = pt.zcu;
1896
1897 var val_space: Value.BigIntSpace = undefined;
1898 const val_bigint = val.toBigInt(&val_space, zcu);
1899
1900 const limbs = try sema.arena.alloc(
1901 std.math.big.Limb,
1902 std.math.big.int.calcTwosCompLimbCount(dest_bits),
1903 );
1904 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1905
1906 result_bigint.truncate(val_bigint, dest_signedness, dest_bits);
1907 return pt.intValue_big(dest_ty, result_bigint.toConst());
1908}
1909
1910fn intShl(
1911 sema: *Sema,
1912 block: *Block,
1913 lhs_ty: Type,
1914 lhs: Value,
1915 rhs: Value,
1916 rhs_src: LazySrcLoc,
1917 vec_idx: ?usize,
1918) !Value {
1919 const pt = sema.pt;
1920 const zcu = pt.zcu;
1921 const info = lhs_ty.intInfo(zcu);
1922
1923 var lhs_space: Value.BigIntSpace = undefined;
1924 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1925
1926 const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu));
1927 if (shift_amt >= info.bits) {
1928 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
1929 }
1930 var result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
1931 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1932 return pt.intValue_big(lhs_ty, result_bigint.toConst());
1933}
1934fn intShlSat(
1935 sema: *Sema,
1936 lhs_ty: Type,
1937 lhs: Value,
1938 rhs: Value,
1939) !Value {
1940 const pt = sema.pt;
1941 const zcu = pt.zcu;
1942 const info = lhs_ty.intInfo(zcu);
1943
1944 var lhs_space: Value.BigIntSpace = undefined;
1945 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1946
1947 const shift_amt: usize = amt: {
1948 if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
1949 if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
1950 }
1951 // We only support ints with up to 2^16 - 1 bits, so this
1952 // shift will fully saturate every non-zero int (assuming
1953 // that `usize` is at least 16 bits wide).
1954 return if (lhs_bigint.eqlZero()) lhs else lhs_ty.maxIntScalar(pt, lhs_ty);
1955 };
1956
1957 const limbs = try sema.arena.alloc(
1958 std.math.big.Limb,
1959 std.math.big.int.calcTwosCompLimbCount(info.bits),
1960 );
1961 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1962 result_bigint.shiftLeftSat(lhs_bigint, shift_amt, info.signedness, info.bits);
1963 return pt.intValue_big(lhs_ty, result_bigint.toConst());
1964}
1965/// If the value overflowed the type and `truncate_result` is `false`, returns a `comptime_int` instead.
1966fn intShlWithOverflow(
1967 sema: *Sema,
1968 block: *Block,
1969 lhs_ty: Type,
1970 lhs: Value,
1971 rhs: Value,
1972 rhs_src: LazySrcLoc,
1973 truncate_result: bool,
1974 vec_idx: ?usize,
1975) !struct { overflow: bool, val: Value } {
1976 const pt = sema.pt;
1977 const zcu = pt.zcu;
1978 const info = lhs_ty.intInfo(zcu);
1979
1980 var lhs_space: Value.BigIntSpace = undefined;
1981 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1982
1983 const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu));
1984 if (shift_amt >= info.bits) {
1985 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
1986 }
1987 var result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
1988 const overflow = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
1989 const result = result: {
1990 if (overflow) {
1991 if (truncate_result) {
1992 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1993 } else {
1994 break :result try pt.intValue_big(.comptime_int, result_bigint.toConst());
1995 }
1996 }
1997 break :result try pt.intValue_big(lhs_ty, result_bigint.toConst());
1998 };
1999 return .{ .overflow = overflow, .val = result };
2000}
2001fn comptimeIntShl(
2002 sema: *Sema,
2003 block: *Block,
2004 lhs: Value,
2005 rhs: Value,
2006 rhs_src: LazySrcLoc,
2007 vec_idx: ?usize,
2008) !Value {
2009 const pt = sema.pt;
2010 const zcu = pt.zcu;
2011 var lhs_space: Value.BigIntSpace = undefined;
2012 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2013 if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
2014 if (std.math.cast(usize, shift_amt_u64)) |shift_amt| {
2015 const result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
2016 return pt.intValue_big(.comptime_int, result_bigint.toConst());
2017 }
2018 }
2019 return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx);
2020}
2021fn intShlInner(sema: *Sema, operand: std.math.big.int.Const, shift_amt: usize) !BigIntMutable {
2022 const limbs = try sema.arena.alloc(
2023 std.math.big.Limb,
2024 operand.limbs.len + (shift_amt / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2025 );
2026 var result: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2027 result.shiftLeft(operand, shift_amt);
2028 return result;
2029}
2030
2031fn intShr(
2032 sema: *Sema,
2033 block: *Block,
2034 lhs_ty: Type,
2035 rhs_ty: Type,
2036 lhs: Value,
2037 rhs: Value,
2038 src: LazySrcLoc,
2039 rhs_src: LazySrcLoc,
2040 op: ShrOp,
2041 vec_idx: ?usize,
2042) !Value {
2043 const pt = sema.pt;
2044 const zcu = pt.zcu;
2045
2046 var lhs_space: Value.BigIntSpace = undefined;
2047 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2048
2049 const shift_amt: usize = if (rhs_ty.toIntern() == .comptime_int_type) amt: {
2050 if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| {
2051 if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
2052 }
2053 if (rhs.compareAllWithZero(.lt, zcu)) {
2054 return sema.failWithNegativeShiftAmount(block, rhs_src, rhs, vec_idx);
2055 } else {
2056 return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx);
2057 }
2058 } else @intCast(rhs.toUnsignedInt(zcu));
2059
2060 if (lhs_ty.toIntern() != .comptime_int_type and shift_amt >= lhs_ty.intInfo(zcu).bits) {
2061 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
2062 }
2063 if (op == .shr_exact and lhs_bigint.ctz(shift_amt) < shift_amt) {
2064 return sema.failWithOwnedErrorMsg(block, msg: {
2065 const msg = try sema.errMsg(src, "exact shift shifted out 1 bits", .{});
2066 errdefer msg.destroy(sema.gpa);
2067 if (vec_idx) |i| try sema.errNote(rhs_src, msg, "when computing vector element at index '{d}'", .{i});
2068 break :msg msg;
2069 });
2070 }
2071 const result_limbs = lhs_bigint.limbs.len -| (shift_amt / (@sizeOf(std.math.big.Limb) * 8));
2072 if (result_limbs == 0) {
2073 // The shift is enough to remove all the bits from the number, which
2074 // means the result is 0 or -1 depending on the sign.
2075 if (lhs_bigint.positive) {
2076 return pt.intValue(lhs_ty, 0);
2077 } else {
2078 return pt.intValue(lhs_ty, -1);
2079 }
2080 }
2081 const limbs = try sema.arena.alloc(std.math.big.Limb, result_limbs);
2082 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2083 result_bigint.shiftRight(lhs_bigint, shift_amt);
2084 return pt.intValue_big(lhs_ty, result_bigint.toConst());
2085}
2086
2087fn intBitReverse(sema: *Sema, val: Value, ty: Type) !Value {
2088 const pt = sema.pt;
2089 const zcu = pt.zcu;
2090 const info = ty.intInfo(zcu);
2091
2092 var val_space: Value.BigIntSpace = undefined;
2093 const val_bigint = val.toBigInt(&val_space, zcu);
2094
2095 const limbs = try sema.arena.alloc(
2096 std.math.big.Limb,
2097 std.math.big.int.calcTwosCompLimbCount(info.bits),
2098 );
2099 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2100 result_bigint.bitReverse(val_bigint, info.signedness, info.bits);
2101 return pt.intValue_big(ty, result_bigint.toConst());
2102}
2103
2104fn intByteSwap(sema: *Sema, val: Value, ty: Type) !Value {
2105 const pt = sema.pt;
2106 const zcu = pt.zcu;
2107 const info = ty.intInfo(zcu);
2108
2109 var val_space: Value.BigIntSpace = undefined;
2110 const val_bigint = val.toBigInt(&val_space, zcu);
2111
2112 const limbs = try sema.arena.alloc(
2113 std.math.big.Limb,
2114 std.math.big.int.calcTwosCompLimbCount(info.bits),
2115 );
2116 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2117 result_bigint.byteSwap(val_bigint, info.signedness, @divExact(info.bits, 8));
2118 return pt.intValue_big(ty, result_bigint.toConst());
2119}
2120
2121fn floatAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2122 const pt = sema.pt;
2123 const zcu = pt.zcu;
2124 const target = zcu.getTarget();
2125 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2126 16 => .{ .f16 = lhs.toFloat(f16, zcu) + rhs.toFloat(f16, zcu) },
2127 32 => .{ .f32 = lhs.toFloat(f32, zcu) + rhs.toFloat(f32, zcu) },
2128 64 => .{ .f64 = lhs.toFloat(f64, zcu) + rhs.toFloat(f64, zcu) },
2129 80 => .{ .f80 = lhs.toFloat(f80, zcu) + rhs.toFloat(f80, zcu) },
2130 128 => .{ .f128 = lhs.toFloat(f128, zcu) + rhs.toFloat(f128, zcu) },
2131 else => unreachable,
2132 };
2133 return .fromInterned(try pt.intern(.{ .float = .{
2134 .ty = ty.toIntern(),
2135 .storage = storage,
2136 } }));
2137}
2138fn floatSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2139 const pt = sema.pt;
2140 const zcu = pt.zcu;
2141 const target = zcu.getTarget();
2142 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2143 16 => .{ .f16 = lhs.toFloat(f16, zcu) - rhs.toFloat(f16, zcu) },
2144 32 => .{ .f32 = lhs.toFloat(f32, zcu) - rhs.toFloat(f32, zcu) },
2145 64 => .{ .f64 = lhs.toFloat(f64, zcu) - rhs.toFloat(f64, zcu) },
2146 80 => .{ .f80 = lhs.toFloat(f80, zcu) - rhs.toFloat(f80, zcu) },
2147 128 => .{ .f128 = lhs.toFloat(f128, zcu) - rhs.toFloat(f128, zcu) },
2148 else => unreachable,
2149 };
2150 return .fromInterned(try pt.intern(.{ .float = .{
2151 .ty = ty.toIntern(),
2152 .storage = storage,
2153 } }));
2154}
2155fn floatMul(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2156 const pt = sema.pt;
2157 const zcu = pt.zcu;
2158 const target = zcu.getTarget();
2159 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2160 16 => .{ .f16 = lhs.toFloat(f16, zcu) * rhs.toFloat(f16, zcu) },
2161 32 => .{ .f32 = lhs.toFloat(f32, zcu) * rhs.toFloat(f32, zcu) },
2162 64 => .{ .f64 = lhs.toFloat(f64, zcu) * rhs.toFloat(f64, zcu) },
2163 80 => .{ .f80 = lhs.toFloat(f80, zcu) * rhs.toFloat(f80, zcu) },
2164 128 => .{ .f128 = lhs.toFloat(f128, zcu) * rhs.toFloat(f128, zcu) },
2165 else => unreachable,
2166 };
2167 return .fromInterned(try pt.intern(.{ .float = .{
2168 .ty = ty.toIntern(),
2169 .storage = storage,
2170 } }));
2171}
2172fn floatDiv(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2173 const pt = sema.pt;
2174 const zcu = pt.zcu;
2175 const target = zcu.getTarget();
2176 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2177 16 => .{ .f16 = lhs.toFloat(f16, zcu) / rhs.toFloat(f16, zcu) },
2178 32 => .{ .f32 = lhs.toFloat(f32, zcu) / rhs.toFloat(f32, zcu) },
2179 64 => .{ .f64 = lhs.toFloat(f64, zcu) / rhs.toFloat(f64, zcu) },
2180 80 => .{ .f80 = lhs.toFloat(f80, zcu) / rhs.toFloat(f80, zcu) },
2181 128 => .{ .f128 = lhs.toFloat(f128, zcu) / rhs.toFloat(f128, zcu) },
2182 else => unreachable,
2183 };
2184 return .fromInterned(try pt.intern(.{ .float = .{
2185 .ty = ty.toIntern(),
2186 .storage = storage,
2187 } }));
2188}
2189fn floatDivTrunc(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2190 const pt = sema.pt;
2191 const zcu = pt.zcu;
2192 const target = zcu.getTarget();
2193 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2194 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2195 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2196 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2197 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2198 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2199 else => unreachable,
2200 };
2201 return .fromInterned(try pt.intern(.{ .float = .{
2202 .ty = ty.toIntern(),
2203 .storage = storage,
2204 } }));
2205}
2206fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2207 const pt = sema.pt;
2208 const zcu = pt.zcu;
2209 const target = zcu.getTarget();
2210 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2211 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2212 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2213 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2214 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2215 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2216 else => unreachable,
2217 };
2218 return .fromInterned(try pt.intern(.{ .float = .{
2219 .ty = ty.toIntern(),
2220 .storage = storage,
2221 } }));
2222}
2223fn floatDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2224 const pt = sema.pt;
2225 const zcu = pt.zcu;
2226 const target = zcu.getTarget();
2227 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2228 16 => .{ .f16 = @divCeil(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2229 32 => .{ .f32 = @divCeil(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2230 64 => .{ .f64 = @divCeil(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2231 80 => .{ .f80 = @divCeil(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2232 128 => .{ .f128 = @divCeil(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2233 else => unreachable,
2234 };
2235 return .fromInterned(try pt.intern(.{ .float = .{
2236 .ty = ty.toIntern(),
2237 .storage = storage,
2238 } }));
2239}
2240fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
2241 const zcu = sema.pt.zcu;
2242 const target = zcu.getTarget();
2243 return switch (ty.floatBits(target)) {
2244 16 => @mod(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) == 0,
2245 32 => @mod(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) == 0,
2246 64 => @mod(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) == 0,
2247 80 => @mod(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) == 0,
2248 128 => @mod(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) == 0,
2249 else => unreachable,
2250 };
2251}
2252fn floatNeg(sema: *Sema, val: Value, ty: Type) !Value {
2253 const pt = sema.pt;
2254 const zcu = pt.zcu;
2255 const target = zcu.getTarget();
2256 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2257 16 => .{ .f16 = -val.toFloat(f16, zcu) },
2258 32 => .{ .f32 = -val.toFloat(f32, zcu) },
2259 64 => .{ .f64 = -val.toFloat(f64, zcu) },
2260 80 => .{ .f80 = -val.toFloat(f80, zcu) },
2261 128 => .{ .f128 = -val.toFloat(f128, zcu) },
2262 else => unreachable,
2263 };
2264 return .fromInterned(try pt.intern(.{ .float = .{
2265 .ty = ty.toIntern(),
2266 .storage = storage,
2267 } }));
2268}
2269fn floatMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2270 const pt = sema.pt;
2271 const zcu = pt.zcu;
2272 const target = zcu.getTarget();
2273 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2274 16 => .{ .f16 = @mod(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2275 32 => .{ .f32 = @mod(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2276 64 => .{ .f64 = @mod(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2277 80 => .{ .f80 = @mod(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2278 128 => .{ .f128 = @mod(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2279 else => unreachable,
2280 };
2281 return .fromInterned(try pt.intern(.{ .float = .{
2282 .ty = ty.toIntern(),
2283 .storage = storage,
2284 } }));
2285}
2286fn floatRem(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2287 const pt = sema.pt;
2288 const zcu = pt.zcu;
2289 const target = zcu.getTarget();
2290 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2291 16 => .{ .f16 = @rem(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2292 32 => .{ .f32 = @rem(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2293 64 => .{ .f64 = @rem(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2294 80 => .{ .f80 = @rem(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2295 128 => .{ .f128 = @rem(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2296 else => unreachable,
2297 };
2298 return .fromInterned(try pt.intern(.{ .float = .{
2299 .ty = ty.toIntern(),
2300 .storage = storage,
2301 } }));
2302}
2303
2304fn intBitwiseNot(sema: *Sema, val: Value, ty: Type) !Value {
2305 const pt = sema.pt;
2306 const zcu = pt.zcu;
2307
2308 if (val.isUndef(zcu)) return pt.undefValue(ty);
2309 switch (ty.toIntern()) {
2310 .bool_type => return .makeBool(!val.toBool()),
2311 .u0_type => return val,
2312 else => {},
2313 }
2314
2315 const info = ty.intInfo(zcu);
2316
2317 var val_space: Value.BigIntSpace = undefined;
2318 const val_bigint = val.toBigInt(&val_space, zcu);
2319 const limbs = try sema.arena.alloc(
2320 std.math.big.Limb,
2321 std.math.big.int.calcTwosCompLimbCount(info.bits),
2322 );
2323 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2324 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2325 return pt.intValue_big(ty, result_bigint.toConst());
2326}
2327/// Given an integer or boolean type, creates an value of that with the bit pattern 0xAA.
2328/// This is used to convert undef values into 0xAA when performing e.g. bitwise operations.
2329/// TODO: Eliminate this function and everything it stands for (related: #19634).
2330fn intValueAa(sema: *Sema, ty: Type) !Value {
2331 const pt = sema.pt;
2332 const zcu = pt.zcu;
2333
2334 if (ty.toIntern() == .bool_type) return .true;
2335 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
2336 const info = ty.intInfo(zcu);
2337
2338 const buf = try sema.arena.alloc(u8, @divCeil(info.bits, 8));
2339 @memset(buf, 0xAA);
2340
2341 const limbs = try sema.arena.alloc(
2342 std.math.big.Limb,
2343 std.math.big.int.calcTwosCompLimbCount(info.bits),
2344 );
2345 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2346 result_bigint.readTwosComplement(buf, info.bits, zcu.getTarget().cpu.arch.endian(), info.signedness);
2347 return pt.intValue_big(ty, result_bigint.toConst());
2348}
2349fn intBitwiseAnd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2350 const pt = sema.pt;
2351 const zcu = pt.zcu;
2352
2353 if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() and rhs.toBool());
2354
2355 var lhs_space: Value.BigIntSpace = undefined;
2356 var rhs_space: Value.BigIntSpace = undefined;
2357 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2358 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2359 const limbs = try sema.arena.alloc(
2360 std.math.big.Limb,
2361 // + 1 for negatives
2362 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2363 );
2364 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2365 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2366 return pt.intValue_big(ty, result_bigint.toConst());
2367}
2368fn intBitwiseNand(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2369 const pt = sema.pt;
2370 const zcu = pt.zcu;
2371
2372 if (ty.toIntern() == .bool_type) return .makeBool(!(lhs.toBool() and rhs.toBool()));
2373 const info = ty.intInfo(zcu);
2374
2375 var lhs_space: Value.BigIntSpace = undefined;
2376 var rhs_space: Value.BigIntSpace = undefined;
2377 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2378 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2379 const limbs = try sema.arena.alloc(
2380 std.math.big.Limb,
2381 @max(
2382 // + 1 for negatives
2383 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2384 std.math.big.int.calcTwosCompLimbCount(info.bits),
2385 ),
2386 );
2387 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2388 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2389 result_bigint.bitNotWrap(result_bigint.toConst(), info.signedness, info.bits);
2390 return pt.intValue_big(ty, result_bigint.toConst());
2391}
2392fn intBitwiseOr(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2393 const pt = sema.pt;
2394 const zcu = pt.zcu;
2395
2396 if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() or rhs.toBool());
2397
2398 var lhs_space: Value.BigIntSpace = undefined;
2399 var rhs_space: Value.BigIntSpace = undefined;
2400 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2401 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2402 const limbs = try sema.arena.alloc(
2403 std.math.big.Limb,
2404 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2405 );
2406 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2407 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2408 return pt.intValue_big(ty, result_bigint.toConst());
2409}
2410fn intBitwiseXor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2411 const pt = sema.pt;
2412 const zcu = pt.zcu;
2413
2414 if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() != rhs.toBool());
2415
2416 var lhs_space: Value.BigIntSpace = undefined;
2417 var rhs_space: Value.BigIntSpace = undefined;
2418 const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2419 const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2420 const limbs = try sema.arena.alloc(
2421 std.math.big.Limb,
2422 // + 1 for negatives
2423 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2424 );
2425 var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2426 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2427 return pt.intValue_big(ty, result_bigint.toConst());
2428}
2429
2430const Sema = @import("../Sema.zig");
2431const Block = Sema.Block;
2432const InternPool = @import("../InternPool.zig");
2433const Type = @import("../Type.zig");
2434const Value = @import("../Value.zig");
2435const Zcu = @import("../Zcu.zig");
2436const CompileError = Zcu.CompileError;
2437const LazySrcLoc = Zcu.LazySrcLoc;
2438
2439const std = @import("std");
2440const assert = std.debug.assert;
2441const Allocator = std.mem.Allocator;
2442const BigIntMutable = std.math.big.int.Mutable;