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