| ... | ... | @@ -5498,6 +5498,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir |
| 5498 | 5498 | } |
| 5499 | 5499 | |
| 5500 | 5500 | static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { |
| 5501 | Error err; |
| 5502 | |
| 5501 | 5503 | LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); |
| 5502 | 5504 | ZigType *array_ptr_type = instruction->ptr->value->type; |
| 5503 | 5505 | assert(array_ptr_type->id == ZigTypeIdPointer); |
| ... | ... | @@ -5506,15 +5508,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI |
| 5506 | 5508 | |
| 5507 | 5509 | bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); |
| 5508 | 5510 | |
| 5511 | // The result is either a slice or a pointer to an array |
| 5509 | 5512 | ZigType *result_type = instruction->base.value->type; |
| 5510 | | if (!type_has_bits(g, result_type)) { |
| 5511 | | return nullptr; |
| 5512 | | } |
| 5513 | 5513 | |
| 5514 | 5514 | // This is not whether the result type has a sentinel, but whether there should be a sentinel check, |
| 5515 | 5515 | // e.g. if they used [a..b :s] syntax. |
| 5516 | 5516 | ZigValue *sentinel = instruction->sentinel; |
| 5517 | 5517 | |
| 5518 | LLVMValueRef slice_start_ptr = nullptr; |
| 5519 | LLVMValueRef len_value = nullptr; |
| 5520 | |
| 5518 | 5521 | if (array_type->id == ZigTypeIdArray || |
| 5519 | 5522 | (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) |
| 5520 | 5523 | { |
| ... | ... | @@ -5528,111 +5531,86 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI |
| 5528 | 5531 | } else { |
| 5529 | 5532 | end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); |
| 5530 | 5533 | } |
| 5534 | |
| 5531 | 5535 | if (want_runtime_safety) { |
| 5536 | // Safety check: start <= end |
| 5532 | 5537 | if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { |
| 5533 | 5538 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5534 | 5539 | } |
| 5535 | | if (instruction->end) { |
| 5536 | | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, |
| 5537 | | array_type->data.array.len, false); |
| 5538 | | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); |
| 5539 | 5540 | |
| 5540 | | if (sentinel != nullptr) { |
| 5541 | | LLVMValueRef indices[] = { |
| 5542 | | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), |
| 5543 | | end_val, |
| 5544 | | }; |
| 5545 | | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 5546 | | add_sentinel_check(g, sentinel_elem_ptr, sentinel); |
| 5547 | | } |
| 5548 | | } |
| 5549 | | } |
| 5550 | | if (!type_has_bits(g, array_type)) { |
| 5551 | | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5541 | // Safety check: the last element of the slice (the sentinel if |
| 5542 | // requested) must be inside the array |
| 5543 | // XXX: Overflow is not checked here... |
| 5544 | const size_t full_len = array_type->data.array.len + |
| 5545 | (array_type->data.array.sentinel != nullptr); |
| 5546 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, |
| 5547 | full_len, false); |
| 5552 | 5548 | |
| 5553 | | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); |
| 5554 | | |
| 5555 | | // TODO if runtime safety is on, store 0xaaaaaaa in ptr field |
| 5556 | | LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); |
| 5557 | | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5558 | | return tmp_struct_ptr; |
| 5549 | LLVMValueRef check_end_val = end_val; |
| 5550 | if (sentinel != nullptr) { |
| 5551 | LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); |
| 5552 | check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); |
| 5553 | } |
| 5554 | add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); |
| 5559 | 5555 | } |
| 5560 | 5556 | |
| 5561 | | LLVMValueRef indices[] = { |
| 5562 | | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), |
| 5563 | | start_val, |
| 5564 | | }; |
| 5565 | | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 5566 | | if (result_type->id == ZigTypeIdPointer) { |
| 5567 | | ir_assert(instruction->result_loc == nullptr, &instruction->base); |
| 5568 | | LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); |
| 5569 | | return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); |
| 5570 | | } else { |
| 5571 | | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5572 | | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); |
| 5573 | | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5557 | bool value_has_bits; |
| 5558 | if ((err = type_has_bits2(g, array_type, &value_has_bits))) |
| 5559 | codegen_report_errors_and_exit(g); |
| 5574 | 5560 | |
| 5575 | | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); |
| 5576 | | LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); |
| 5577 | | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5561 | if (value_has_bits) { |
| 5562 | if (want_runtime_safety && sentinel != nullptr) { |
| 5563 | LLVMValueRef indices[] = { |
| 5564 | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), |
| 5565 | end_val, |
| 5566 | }; |
| 5567 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 5568 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); |
| 5569 | } |
| 5578 | 5570 | |
| 5579 | | return tmp_struct_ptr; |
| 5571 | LLVMValueRef indices[] = { |
| 5572 | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), |
| 5573 | start_val, |
| 5574 | }; |
| 5575 | slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 5580 | 5576 | } |
| 5577 | |
| 5578 | len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); |
| 5581 | 5579 | } else if (array_type->id == ZigTypeIdPointer) { |
| 5582 | 5580 | assert(array_type->data.pointer.ptr_len != PtrLenSingle); |
| 5583 | 5581 | LLVMValueRef start_val = ir_llvm_value(g, instruction->start); |
| 5584 | 5582 | LLVMValueRef end_val = ir_llvm_value(g, instruction->end); |
| 5585 | 5583 | |
| 5586 | 5584 | if (want_runtime_safety) { |
| 5585 | // Safety check: start <= end |
| 5587 | 5586 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5588 | | if (sentinel != nullptr) { |
| 5587 | } |
| 5588 | |
| 5589 | bool value_has_bits; |
| 5590 | if ((err = type_has_bits2(g, array_type, &value_has_bits))) |
| 5591 | codegen_report_errors_and_exit(g); |
| 5592 | |
| 5593 | if (value_has_bits) { |
| 5594 | if (want_runtime_safety && sentinel != nullptr) { |
| 5589 | 5595 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); |
| 5590 | 5596 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); |
| 5591 | 5597 | } |
| 5592 | | } |
| 5593 | 5598 | |
| 5594 | | if (!type_has_bits(g, array_type)) { |
| 5595 | | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5596 | | size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; |
| 5597 | | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); |
| 5598 | | LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); |
| 5599 | | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5600 | | return tmp_struct_ptr; |
| 5599 | slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); |
| 5601 | 5600 | } |
| 5602 | 5601 | |
| 5603 | | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); |
| 5604 | | if (result_type->id == ZigTypeIdPointer) { |
| 5605 | | ir_assert(instruction->result_loc == nullptr, &instruction->base); |
| 5606 | | LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); |
| 5607 | | return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); |
| 5608 | | } |
| 5609 | | |
| 5610 | | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5611 | | |
| 5612 | | size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; |
| 5613 | | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); |
| 5614 | | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5615 | | |
| 5616 | | size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; |
| 5617 | | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); |
| 5618 | | LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); |
| 5619 | | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5620 | | |
| 5621 | | return tmp_struct_ptr; |
| 5622 | | |
| 5602 | len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); |
| 5623 | 5603 | } else if (array_type->id == ZigTypeIdStruct) { |
| 5624 | 5604 | assert(array_type->data.structure.special == StructSpecialSlice); |
| 5625 | 5605 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 5626 | 5606 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 5627 | 5607 | |
| 5628 | | size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; |
| 5629 | | assert(ptr_index != SIZE_MAX); |
| 5630 | | size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; |
| 5631 | | assert(len_index != SIZE_MAX); |
| 5608 | const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index; |
| 5609 | assert(gen_len_index != SIZE_MAX); |
| 5632 | 5610 | |
| 5633 | 5611 | LLVMValueRef prev_end = nullptr; |
| 5634 | 5612 | if (!instruction->end || want_runtime_safety) { |
| 5635 | | LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); |
| 5613 | LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, ""); |
| 5636 | 5614 | prev_end = gen_load_untyped(g, src_len_ptr, 0, false, ""); |
| 5637 | 5615 | } |
| 5638 | 5616 | |
| ... | ... | @@ -5644,41 +5622,104 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI |
| 5644 | 5622 | end_val = prev_end; |
| 5645 | 5623 | } |
| 5646 | 5624 | |
| 5647 | | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); |
| 5648 | | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); |
| 5625 | ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; |
| 5649 | 5626 | |
| 5650 | 5627 | if (want_runtime_safety) { |
| 5651 | 5628 | assert(prev_end); |
| 5629 | // Safety check: start <= end |
| 5652 | 5630 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5653 | | if (instruction->end) { |
| 5654 | | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); |
| 5655 | 5631 | |
| 5656 | | if (sentinel != nullptr) { |
| 5657 | | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); |
| 5658 | | add_sentinel_check(g, sentinel_elem_ptr, sentinel); |
| 5659 | | } |
| 5632 | // Safety check: the sentinel counts as one more element |
| 5633 | // XXX: Overflow is not checked here... |
| 5634 | LLVMValueRef check_prev_end = prev_end; |
| 5635 | if (ptr_field_type->data.pointer.sentinel != nullptr) { |
| 5636 | LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); |
| 5637 | check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, ""); |
| 5638 | } |
| 5639 | LLVMValueRef check_end_val = end_val; |
| 5640 | if (sentinel != nullptr) { |
| 5641 | LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); |
| 5642 | check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); |
| 5660 | 5643 | } |
| 5644 | |
| 5645 | add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end); |
| 5661 | 5646 | } |
| 5662 | 5647 | |
| 5663 | | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); |
| 5664 | | if (result_type->id == ZigTypeIdPointer) { |
| 5665 | | ir_assert(instruction->result_loc == nullptr, &instruction->base); |
| 5666 | | LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); |
| 5667 | | return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); |
| 5668 | | } else { |
| 5669 | | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5670 | | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); |
| 5671 | | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5648 | bool ptr_has_bits; |
| 5649 | if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits))) |
| 5650 | codegen_report_errors_and_exit(g); |
| 5651 | |
| 5652 | if (ptr_has_bits) { |
| 5653 | const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; |
| 5654 | assert(gen_ptr_index != SIZE_MAX); |
| 5672 | 5655 | |
| 5673 | | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); |
| 5674 | | LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); |
| 5675 | | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5656 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, ""); |
| 5657 | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); |
| 5676 | 5658 | |
| 5677 | | return tmp_struct_ptr; |
| 5659 | if (sentinel != nullptr) { |
| 5660 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); |
| 5661 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); |
| 5662 | } |
| 5663 | |
| 5664 | slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); |
| 5678 | 5665 | } |
| 5666 | |
| 5667 | len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); |
| 5679 | 5668 | } else { |
| 5680 | 5669 | zig_unreachable(); |
| 5681 | 5670 | } |
| 5671 | |
| 5672 | bool result_has_bits; |
| 5673 | if ((err = type_has_bits2(g, result_type, &result_has_bits))) |
| 5674 | codegen_report_errors_and_exit(g); |
| 5675 | |
| 5676 | // Nothing to do, we're only interested in the bound checks emitted above |
| 5677 | if (!result_has_bits) |
| 5678 | return nullptr; |
| 5679 | |
| 5680 | // The starting pointer for the slice may be null in case of zero-sized |
| 5681 | // arrays, the length value is always defined. |
| 5682 | assert(len_value != nullptr); |
| 5683 | |
| 5684 | // The slice decays into a pointer to an array, the size is tracked in the |
| 5685 | // type itself |
| 5686 | if (result_type->id == ZigTypeIdPointer) { |
| 5687 | ir_assert(instruction->result_loc == nullptr, &instruction->base); |
| 5688 | LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); |
| 5689 | |
| 5690 | if (slice_start_ptr != nullptr) { |
| 5691 | return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); |
| 5692 | } |
| 5693 | |
| 5694 | return LLVMGetUndef(result_ptr_type); |
| 5695 | } |
| 5696 | |
| 5697 | ir_assert(instruction->result_loc != nullptr, &instruction->base); |
| 5698 | // Create a new slice |
| 5699 | LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); |
| 5700 | |
| 5701 | ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; |
| 5702 | |
| 5703 | // The slice may not have a pointer at all if it points to a zero-sized type |
| 5704 | const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; |
| 5705 | if (gen_ptr_index != SIZE_MAX) { |
| 5706 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); |
| 5707 | if (slice_start_ptr != nullptr) { |
| 5708 | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5709 | } else if (want_runtime_safety) { |
| 5710 | gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); |
| 5711 | } else { |
| 5712 | gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false); |
| 5713 | } |
| 5714 | } |
| 5715 | |
| 5716 | const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; |
| 5717 | assert(gen_len_index != SIZE_MAX); |
| 5718 | |
| 5719 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); |
| 5720 | gen_store_untyped(g, len_value, len_field_ptr, 0, false); |
| 5721 | |
| 5722 | return tmp_struct_ptr; |
| 5682 | 5723 | } |
| 5683 | 5724 | |
| 5684 | 5725 | static LLVMValueRef get_trap_fn_val(CodeGen *g) { |