authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-03-22 20:20:36+01:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-04-03 10:58:39+02:00
log855edd2949c6b3b36be4c3ba8d30f174ff8b8db1
tree7b842d03deedaa6ba657b59c99f80bc993a2da9b
parent0dbf8aaab83d7387568d6387c6cbd263e04c7397

ir: Rewrite the bound checks in slice operator

Closes #4777

1 files changed, 138 insertions(+), 97 deletions(-)

src/codegen.cpp+138-97
......@@ -5408,6 +5408,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir
54085408}
54095409
54105410static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) {
5411 Error err;
5412
54115413 LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr);
54125414 ZigType *array_ptr_type = instruction->ptr->value->type;
54135415 assert(array_ptr_type->id == ZigTypeIdPointer);
......@@ -5416,15 +5418,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
54165418
54175419 bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
54185420
5421 // The result is either a slice or a pointer to an array
54195422 ZigType *result_type = instruction->base.value->type;
5420 if (!type_has_bits(g, result_type)) {
5421 return nullptr;
5422 }
54235423
54245424 // This is not whether the result type has a sentinel, but whether there should be a sentinel check,
54255425 // e.g. if they used [a..b :s] syntax.
54265426 ZigValue *sentinel = instruction->sentinel;
54275427
5428 LLVMValueRef slice_start_ptr = nullptr;
5429 LLVMValueRef len_value = nullptr;
5430
54285431 if (array_type->id == ZigTypeIdArray ||
54295432 (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
54305433 {
......@@ -5438,111 +5441,86 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
54385441 } else {
54395442 end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false);
54405443 }
5444
54415445 if (want_runtime_safety) {
5446 // Safety check: start <= end
54425447 if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) {
54435448 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
54445449 }
5445 if (instruction->end) {
5446 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,
5447 array_type->data.array.len, false);
5448 add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
54495450
5450 if (sentinel != nullptr) {
5451 LLVMValueRef indices[] = {
5452 LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
5453 end_val,
5454 };
5455 LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
5456 add_sentinel_check(g, sentinel_elem_ptr, sentinel);
5457 }
5458 }
5459 }
5460 if (!type_has_bits(g, array_type)) {
5461 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5451 // Safety check: the last element of the slice (the sentinel if
5452 // requested) must be inside the array
5453 // XXX: Overflow is not checked here...
5454 const size_t full_len = array_type->data.array.len +
5455 (array_type->data.array.sentinel != nullptr);
5456 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,
5457 full_len, false);
54625458
5463 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
5464
5465 // TODO if runtime safety is on, store 0xaaaaaaa in ptr field
5466 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5467 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5468 return tmp_struct_ptr;
5459 LLVMValueRef check_end_val = end_val;
5460 if (sentinel != nullptr) {
5461 LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false);
5462 check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, "");
5463 }
5464 add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
54695465 }
54705466
5471 LLVMValueRef indices[] = {
5472 LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
5473 start_val,
5474 };
5475 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
5476 if (result_type->id == ZigTypeIdPointer) {
5477 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5478 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5479 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5480 } else {
5481 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5482 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
5483 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5467 bool value_has_bits;
5468 if ((err = type_has_bits2(g, array_type, &value_has_bits)))
5469 codegen_report_errors_and_exit(g);
54845470
5485 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
5486 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5487 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5471 if (value_has_bits) {
5472 if (want_runtime_safety && sentinel != nullptr) {
5473 LLVMValueRef indices[] = {
5474 LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
5475 end_val,
5476 };
5477 LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
5478 add_sentinel_check(g, sentinel_elem_ptr, sentinel);
5479 }
54885480
5489 return tmp_struct_ptr;
5481 LLVMValueRef indices[] = {
5482 LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
5483 start_val,
5484 };
5485 slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
54905486 }
5487
5488 len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, "");
54915489 } else if (array_type->id == ZigTypeIdPointer) {
54925490 assert(array_type->data.pointer.ptr_len != PtrLenSingle);
54935491 LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
54945492 LLVMValueRef end_val = ir_llvm_value(g, instruction->end);
54955493
54965494 if (want_runtime_safety) {
5495 // Safety check: start <= end
54975496 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
5498 if (sentinel != nullptr) {
5497 }
5498
5499 bool value_has_bits;
5500 if ((err = type_has_bits2(g, array_type, &value_has_bits)))
5501 codegen_report_errors_and_exit(g);
5502
5503 if (value_has_bits) {
5504 if (want_runtime_safety && sentinel != nullptr) {
54995505 LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, "");
55005506 add_sentinel_check(g, sentinel_elem_ptr, sentinel);
55015507 }
5502 }
55035508
5504 if (!type_has_bits(g, array_type)) {
5505 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5506 size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
5507 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
5508 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5509 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5510 return tmp_struct_ptr;
5509 slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
55115510 }
55125511
5513 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
5514 if (result_type->id == ZigTypeIdPointer) {
5515 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5516 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5517 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5518 }
5519
5520 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5521
5522 size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index;
5523 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
5524 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5525
5526 size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
5527 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
5528 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5529 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5530
5531 return tmp_struct_ptr;
5532
5512 len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, "");
55335513 } else if (array_type->id == ZigTypeIdStruct) {
55345514 assert(array_type->data.structure.special == StructSpecialSlice);
55355515 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
55365516 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
55375517
5538 size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index;
5539 assert(ptr_index != SIZE_MAX);
5540 size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index;
5541 assert(len_index != SIZE_MAX);
5518 const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index;
5519 assert(gen_len_index != SIZE_MAX);
55425520
55435521 LLVMValueRef prev_end = nullptr;
55445522 if (!instruction->end || want_runtime_safety) {
5545 LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, "");
5523 LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, "");
55465524 prev_end = gen_load_untyped(g, src_len_ptr, 0, false, "");
55475525 }
55485526
......@@ -5554,41 +5532,104 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
55545532 end_val = prev_end;
55555533 }
55565534
5557 LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");
5558 LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, "");
5535 ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
55595536
55605537 if (want_runtime_safety) {
55615538 assert(prev_end);
5539 // Safety check: start <= end
55625540 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
5563 if (instruction->end) {
5564 add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
55655541
5566 if (sentinel != nullptr) {
5567 LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, "");
5568 add_sentinel_check(g, sentinel_elem_ptr, sentinel);
5569 }
5542 // Safety check: the sentinel counts as one more element
5543 // XXX: Overflow is not checked here...
5544 LLVMValueRef check_prev_end = prev_end;
5545 if (ptr_field_type->data.pointer.sentinel != nullptr) {
5546 LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false);
5547 check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, "");
5548 }
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, "");
55705553 }
5554
5555 add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end);
55715556 }
55725557
5573 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
5574 if (result_type->id == ZigTypeIdPointer) {
5575 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5576 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5577 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5578 } else {
5579 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5580 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
5581 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5558 bool ptr_has_bits;
5559 if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits)))
5560 codegen_report_errors_and_exit(g);
5561
5562 if (ptr_has_bits) {
5563 const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index;
5564 assert(gen_ptr_index != SIZE_MAX);
55825565
5583 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
5584 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5585 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5566 LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, "");
5567 LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, "");
55865568
5587 return tmp_struct_ptr;
5569 if (sentinel != nullptr) {
5570 LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, "");
5571 add_sentinel_check(g, sentinel_elem_ptr, sentinel);
5572 }
5573
5574 slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
55885575 }
5576
5577 len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, "");
55895578 } else {
55905579 zig_unreachable();
55915580 }
5581
5582 bool result_has_bits;
5583 if ((err = type_has_bits2(g, result_type, &result_has_bits)))
5584 codegen_report_errors_and_exit(g);
5585
5586 // Nothing to do, we're only interested in the bound checks emitted above
5587 if (!result_has_bits)
5588 return nullptr;
5589
5590 // The starting pointer for the slice may be null in case of zero-sized
5591 // arrays, the length value is always defined.
5592 assert(len_value != nullptr);
5593
5594 // The slice decays into a pointer to an array, the size is tracked in the
5595 // type itself
5596 if (result_type->id == ZigTypeIdPointer) {
5597 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5598 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5599
5600 if (slice_start_ptr != nullptr) {
5601 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5602 }
5603
5604 return LLVMGetUndef(result_ptr_type);
5605 }
5606
5607 ir_assert(instruction->result_loc != nullptr, &instruction->base);
5608 // Create a new slice
5609 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5610
5611 ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry;
5612
5613 // The slice may not have a pointer at all if it points to a zero-sized type
5614 const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index;
5615 if (gen_ptr_index != SIZE_MAX) {
5616 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
5617 if (slice_start_ptr != nullptr) {
5618 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5619 } else if (want_runtime_safety) {
5620 gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr);
5621 } else {
5622 gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false);
5623 }
5624 }
5625
5626 const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
5627 assert(gen_len_index != SIZE_MAX);
5628
5629 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
5630 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5631
5632 return tmp_struct_ptr;
55925633}
55935634
55945635static LLVMValueRef get_trap_fn_val(CodeGen *g) {