Skip to content

Commit bb5318b

Browse files
Adding support for Machine Integer version of Bytes, List, and MInt functions (#1209)
This PR introduces the concrete implementation of the following new Machine Integer hooks: ```k 1. syntax {Width} MInt{Width} ::=MInt{Width} "^MInt" MInt{Width} 2. syntax {Width} Bytes ::= MInt2Bytes(MInt{Width}) 3. syntax {Width} MInt{Width} ::= Bytes2MInt(Bytes) 4. syntax {Width} MInt{Width} ::= lengthBytes(Bytes) 5. syntax {Width} Bytes ::= padRightBytes(Bytes, length: MInt{Width}, value: MInt{Width}) 6. syntax {Width} Bytes ::= padLeftBytes(Bytes, length: MInt{Width}, value: MInt{Width}) 7. syntax {Width} Bytes ::= replaceAtBytes(dest: Bytes, index: MInt{Width}, src: Bytes) 8. syntax {Width} Bytes ::= substrBytes(Bytes, startIndex: MInt{Width}, endIndex: MInt{Width}) 9. syntax {Width} MInt{Width} ::= Bytes "[" MInt{Width} "]" 10. syntax {Width} Bytes ::= Bytes "[" index: MInt{Width} "<-" value: MInt{Width} "]" 11. syntax {Width} MInt{Width} ::= size(List) 12. syntax {Width} List ::= List "[" index: MInt{Width} "<-" value: KItem "]" 13. syntax {Width} KItem ::= List "[" MInt{Width} "]" ``` Most of these hooks have an initial implementation for `64` and `256-bit` Integers, being the former defined in C++ in its respective collection file while the latter is defined in the `llvm_header.inc` file, as C++ doesn't support 256-bit integer natively. The only exceptions for now are 12 and 13, which are only implemented for 64-bit, as we have the `immer` limitation that the index should be a `in64_t`.
1 parent 10a5322 commit bb5318b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+40952
-20
lines changed

config/llvm_header.inc

Lines changed: 360 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,41 @@ declare void @print_configuration(ptr, ptr)
8181
; finish_rewriting.ll
8282
8383
declare i64 @__gmpz_get_ui(ptr)
84+
declare ptr @kore_alloc(i64)
85+
declare void @memcpy(ptr, ptr, i64)
86+
declare void @error_on_get(i64, i64)
87+
declare void @error_on_start_substr(i64, i64)
88+
declare void @error_on_end_substr(ptr, i64)
89+
declare void @integer_overflow(i64)
90+
declare void @buffer_overflow_replace_at(i64, i64, i64)
91+
declare void @buffer_overflow_update(i64, i64)
92+
declare void @error_on_update(i64)
93+
declare i1 @hook_BYTES_mutableBytesEnabled()
94+
declare i64 @hook_BYTES_length64(ptr)
95+
96+
define ptr @copy_if_needed_in_llvm(ptr %b) {
97+
entry:
98+
%mutable_bytes_enabled = call i1 @hook_BYTES_mutableBytesEnabled()
99+
br i1 %mutable_bytes_enabled, label %return_b, label %copy_needed
100+
101+
return_b:
102+
ret ptr %b
103+
104+
copy_needed:
105+
%len = call i64 @hook_BYTES_length64(ptr %b)
106+
%len_bytes = mul i64 %len, 8 ; 8 bytes per byte
107+
%alloc_size = add i64 %len_bytes, 40 ; 8 bytes for header, rest for data
108+
%alloc = call ptr @kore_alloc(i64 %alloc_size)
109+
110+
store i64 %len, ptr %alloc
111+
112+
%data_ptr = getelementptr i8, ptr %alloc, i64 8
113+
%b_data_ptr = getelementptr i8, ptr %b, i64 8
114+
115+
call void @memcpy(ptr %data_ptr, ptr %b_data_ptr, i64 %len_bytes)
116+
117+
ret ptr %data_ptr
118+
}
84119
85120
@exit_int_0 = constant %mpz { i32 0, i32 0, ptr getelementptr inbounds ([0 x i64], ptr @exit_int_0_limbs, i32 0, i32 0) }
86121
@exit_int_0_limbs = constant [0 x i64] zeroinitializer
@@ -177,7 +212,8 @@ declare noalias ptr @kore_alloc_integer(i64)
177212
178213
; string_equal.ll
179214
180-
declare i32 @memcmp(ptr, ptr, i64);
215+
declare i32 @memcmp(ptr, ptr, i64)
216+
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
181217
182218
define i1 @string_equal(ptr %str1, ptr %str2, i64 %len1, i64 %len2) {
183219
%len_eq = icmp eq i64 %len1, %len2
@@ -245,6 +281,329 @@ define void @take_search_step(ptr %subject) {
245281
ret void
246282
}
247283
284+
; MInt Functions
285+
286+
define i256 @hook_LIST_size256(ptr %l) {
287+
entry:
288+
%len_ptr = getelementptr %list, ptr %l, i64 0, i32 0
289+
%len = load i64, ptr %len_ptr
290+
%len_256 = zext i64 %len to i256
291+
ret i256 %len_256
292+
}
293+
294+
define i256 @hook_BYTES_get256(ptr %b, i256 %off) {
295+
entry:
296+
; Check for buffer overflow
297+
%len = call i64 @hook_BYTES_length64(ptr %b)
298+
%len_256 = zext i64 %len to i256
299+
%off_lt_len = icmp ult i256 %off, %len_256
300+
br i1 %off_lt_len, label %valid_off, label %overflow
301+
overflow:
302+
%off_trunc = trunc i256 %off to i64
303+
call void @error_on_get(i64 %off_trunc, i64 %len)
304+
unreachable
305+
valid_off:
306+
; Load the byte at the specified offset
307+
%data_ptr = getelementptr %string, ptr %b, i256 0, i32 1, i256 0
308+
%byte_ptr = getelementptr i8, ptr %data_ptr, i256 %off
309+
%byte_value = load i8, ptr %byte_ptr
310+
311+
; Return the byte value as an i256
312+
%result = zext i8 %byte_value to i256
313+
ret i256 %result
314+
}
315+
316+
define ptr @hook_BYTES_substr256(ptr %input, i256 %start, i256 %end) {
317+
entry:
318+
%end_lt_start = icmp ult i256 %end, %start
319+
br i1 %end_lt_start, label %error, label %check_length
320+
error:
321+
%end_trunc = trunc i256 %end to i64
322+
%start_trunc = trunc i256 %start to i64
323+
call void @error_on_start_substr(i64 %start_trunc, i64 %end_trunc)
324+
unreachable
325+
check_length:
326+
%input_len = call i64 @hook_BYTES_length64(ptr %input)
327+
%input_len_256 = zext i64 %input_len to i256
328+
%end_gt_input_len = icmp ugt i256 %end, %input_len_256
329+
br i1 %end_gt_input_len, label %error_length, label %calculate_length
330+
error_length:
331+
%end_trunc2 = trunc i256 %end to i64
332+
call void @error_on_end_substr(ptr %input, i64 %end_trunc2)
333+
unreachable
334+
calculate_length:
335+
%len = sub i256 %end, %start
336+
337+
%bytes_len = mul i256 %len, 8
338+
%alloc_size = add i256 40, %bytes_len
339+
%alloc_size_i64 = trunc i256 %alloc_size to i64
340+
%alloc = call ptr @kore_alloc(i64 %alloc_size_i64)
341+
342+
%len_i64 = trunc i256 %len to i64
343+
%bytes_ptr = getelementptr [40 x i8], ptr %alloc, i64 0, i64 0
344+
store i64 %len_i64, ptr %bytes_ptr
345+
346+
%data_ptr = getelementptr [40 x i8], ptr %alloc, i64 0, i64 8
347+
%input_data_ptr = getelementptr %string, ptr %input, i256 0, i32 1, i256 0
348+
%start_ptr = getelementptr i8, ptr %input_data_ptr, i256 %start
349+
%end_ptr = getelementptr i8, ptr %input_data_ptr, i256 %end
350+
call void @memcpy(ptr %data_ptr, ptr %start_ptr, i64 %len_i64)
351+
352+
%result = bitcast ptr %alloc to ptr
353+
ret ptr %result
354+
}
355+
356+
define i256 @hook_BYTES_length256(ptr %b) {
357+
entry:
358+
%len = call i64 @hook_BYTES_length64(ptr %b)
359+
%len_256 = zext i64 %len to i256
360+
ret i256 %len_256
361+
}
362+
363+
define ptr @hook_BYTES_padRight256(ptr %b, i256 %length, i256 %v) {
364+
entry:
365+
%len = call i64 @hook_BYTES_length64(ptr %b)
366+
%len_256 = zext i64 %len to i256
367+
%length_gt_len = icmp ugt i256 %length, %len_256
368+
br i1 %length_gt_len, label %pad, label %return_b
369+
370+
return_b:
371+
ret ptr %b
372+
373+
pad:
374+
%v_gt_255 = icmp ugt i256 %v, 255
375+
br i1 %v_gt_255, label %error, label %allocate
376+
377+
error:
378+
%v_trunc = trunc i256 %v to i64
379+
call void @integer_overflow(i64 %v_trunc)
380+
unreachable
381+
382+
allocate:
383+
%bytes_len = mul i256 %length, 8
384+
%alloc_size = add i256 %bytes_len, 40 ; 8 bytes for header, rest for data
385+
%alloc_size_i64 = trunc i256 %alloc_size to i64
386+
%alloc = call ptr @kore_alloc(i64 %alloc_size_i64)
387+
388+
%length_i64 = trunc i256 %length to i64
389+
store i64 %length_i64, ptr %alloc
390+
391+
%data_ptr = getelementptr i8, ptr %alloc, i64 8
392+
%b_data_ptr = getelementptr i8, ptr %b, i64 8
393+
call void @memcpy(ptr %data_ptr, ptr %b_data_ptr, i64 %len)
394+
395+
%remaining_bytes = sub i64 %length_i64, %len
396+
%fill_ptr = getelementptr i8, ptr %data_ptr, i64 %len
397+
%v_i8 = trunc i256 %v to i8
398+
call void @llvm.memset.p0.i64(ptr %fill_ptr, i8 %v_i8, i64 %remaining_bytes, i1 false)
399+
400+
%result = bitcast ptr %alloc to ptr
401+
ret ptr %result
402+
}
403+
404+
define ptr @hook_BYTES_padLeft256(ptr %b, i256 %length, i256 %v) {
405+
entry:
406+
%len = call i64 @hook_BYTES_length64(ptr %b)
407+
%len_256 = zext i64 %len to i256
408+
%length_gt_len = icmp ugt i256 %length, %len_256
409+
br i1 %length_gt_len, label %pad, label %return_b
410+
411+
return_b:
412+
ret ptr %b
413+
414+
pad:
415+
%v_gt_255 = icmp ugt i256 %v, 255
416+
br i1 %v_gt_255, label %error, label %allocate
417+
418+
error:
419+
%v_trunc = trunc i256 %v to i64
420+
call void @integer_overflow(i64 %v_trunc)
421+
unreachable
422+
423+
allocate:
424+
%bytes_len = mul i256 %length, 8
425+
%alloc_size = add i256 %bytes_len, 40 ; 8 bytes for header, rest for data
426+
%alloc_size_i64 = trunc i256 %alloc_size to i64
427+
%alloc = call ptr @kore_alloc(i64 %alloc_size_i64)
428+
429+
%length_i64 = trunc i256 %length to i64
430+
store i64 %length_i64, ptr %alloc
431+
%data_ptr = getelementptr i8, ptr %alloc, i64 8
432+
%b_data_ptr = getelementptr i8, ptr %b, i64 8
433+
%remaining_bytes = sub i64 %length_i64, %len
434+
%fill_ptr = getelementptr i8, ptr %data_ptr, i64 0
435+
%v_i8 = trunc i256 %v to i8
436+
call void @llvm.memset.p0.i64(ptr %fill_ptr, i8 %v_i8, i64 %remaining_bytes, i1 false)
437+
438+
%b_data_end_ptr = getelementptr i8, ptr %data_ptr, i64 %remaining_bytes
439+
call void @memcpy(ptr %b_data_end_ptr, ptr %b_data_ptr, i64 %len)
440+
441+
%result = bitcast ptr %alloc to ptr
442+
ret ptr %result
443+
}
444+
445+
define ptr @hook_BYTES_replaceAt256(ptr %dest, i256 %index, ptr %src) {
446+
entry:
447+
call ptr @copy_if_needed_in_llvm(ptr %dest)
448+
449+
%dest_len = call i64 @hook_BYTES_length64(ptr %dest)
450+
%dest_len_256 = zext i64 %dest_len to i256
451+
452+
%src_len = call i64 @hook_BYTES_length64(ptr %src)
453+
%src_len_256 = zext i64 %src_len to i256
454+
455+
%index_sum_dest_src = add i256 %index, %src_len_256
456+
%index_gt_dest_len = icmp ugt i256 %index_sum_dest_src, %dest_len_256
457+
458+
br i1 %index_gt_dest_len, label %error, label %copy_data
459+
460+
error:
461+
%index_trunc = trunc i256 %index to i64
462+
%dest_len_trunc = trunc i256 %dest_len_256 to i64
463+
%src_len_trunc = trunc i256 %src_len_256 to i64
464+
call void @buffer_overflow_replace_at(i64 %index_trunc, i64 %dest_len_trunc, i64 %src_len_trunc)
465+
unreachable
466+
467+
copy_data:
468+
%data_ptr = getelementptr i8, ptr %dest, i64 8
469+
%src_data_ptr = getelementptr i8, ptr %src, i64 8
470+
%index_i64 = trunc i256 %index to i64
471+
%dest_offset_ptr = getelementptr i8, ptr %data_ptr, i64 %index_i64
472+
call void @memcpy(ptr %dest_offset_ptr, ptr %src_data_ptr, i64 %src_len)
473+
474+
; Return the modified destination
475+
ret ptr %dest
476+
}
477+
478+
define ptr @hook_MINT_MInt2Bytes(i256 %mint) {
479+
entry:
480+
%alloc = call ptr @kore_alloc(i64 40) ; 8 bytes for header, 32 bytes for MInt
481+
482+
; store 32 as lenght of the MInt in the fist 8 bytes
483+
%bytes_ptr = getelementptr [40 x i8], ptr %alloc, i64 0, i64 0
484+
store i64 32, ptr %bytes_ptr
485+
486+
; store the MInt in the next 32 bytes
487+
%mint_ptr = getelementptr [40 x i8], ptr %alloc, i64 0, i64 8
488+
%mint_vector = bitcast i256 %mint to <32 x i8>
489+
%reversed_mint = shufflevector <32 x i8> %mint_vector, <32 x i8> undef, <32 x i32>
490+
<i32 31, i32 30, i32 29, i32 28, i32 27, i32 26, i32 25, i32 24,
491+
i32 23, i32 22, i32 21, i32 20, i32 19, i32 18, i32 17, i32 16,
492+
i32 15, i32 14, i32 13, i32 12, i32 11, i32 10, i32 9, i32 8,
493+
i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0>
494+
495+
; Store the reversed MInt bytes in the allocated memory
496+
%reversed_mint_ptr = alloca <32 x i8>
497+
store <32 x i8> %reversed_mint, ptr %reversed_mint_ptr
498+
call void @memcpy(ptr %mint_ptr, ptr %reversed_mint_ptr, i64 32)
499+
500+
; Return the pointer to the bytes
501+
ret ptr %alloc
502+
}
503+
504+
define i256 @hook_MINT_Bytes2MInt(ptr %bytes) {
505+
entry:
506+
%alloc = alloca [32 x i8]
507+
%bytes_ptr = getelementptr [32 x i8], ptr %alloc, i64 0, i64 0
508+
509+
; The first 8 bytes are the used by the header, so we skip them as
510+
; we assume we will always have a 256-bit MInt
511+
%bytes_contents = getelementptr i8, ptr %bytes, i64 8
512+
call void @memcpy(ptr %bytes_ptr, ptr %bytes_contents, i64 32)
513+
514+
; Convert the bytes to a 256-bit integer
515+
%mint = load i256, ptr %bytes_ptr
516+
517+
; Reverse the byte order to convert from little-endian to big-endian
518+
%mint_vector = bitcast i256 %mint to <32 x i8>
519+
%reversed_mint = shufflevector <32 x i8> %mint_vector, <32 x i8> undef, <32 x i32>
520+
<i32 31, i32 30, i32 29, i32 28, i32 27, i32 26, i32 25, i32 24,
521+
i32 23, i32 22, i32 21, i32 20, i32 19, i32 18, i32 17, i32 16,
522+
i32 15, i32 14, i32 13, i32 12, i32 11, i32 10, i32 9, i32 8,
523+
i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0>
524+
525+
; Convert the reversed vector back to i256
526+
%result = bitcast <32 x i8> %reversed_mint to i256
527+
ret i256 %result
528+
}
529+
530+
define ptr @hook_BYTES_update256(ptr %b, i256 %index, i256 %value) {
531+
entry:
532+
call ptr @copy_if_needed_in_llvm(ptr %b)
533+
534+
%len = call i64 @hook_BYTES_length64(ptr %b)
535+
%len_256 = zext i64 %len to i256
536+
537+
%index_uge_len = icmp uge i256 %index, %len_256
538+
br i1 %index_uge_len, label %error, label %update_value
539+
540+
error:
541+
%index_trunc = trunc i256 %index to i64
542+
%len_trunc = trunc i256 %len_256 to i64
543+
call void @buffer_overflow_update(i64 %index_trunc, i64 %len_trunc)
544+
unreachable
545+
546+
update_value:
547+
%value_ge_255 = icmp ugt i256 %value, 255
548+
br i1 %value_ge_255, label %error_value, label %set_value
549+
550+
error_value:
551+
%value_trunc = trunc i256 %value to i64
552+
call void @error_on_update(i64 %value_trunc)
553+
unreachable
554+
555+
set_value:
556+
%data_ptr = getelementptr i8, ptr %b, i64 8
557+
%index_i64 = trunc i256 %index to i64
558+
%byte_ptr = getelementptr i8, ptr %data_ptr, i64 %index_i64
559+
%value_i8 = trunc i256 %value to i8
560+
store i8 %value_i8, ptr %byte_ptr
561+
562+
; Return the modified buffer
563+
ret ptr %b
564+
}
565+
566+
567+
define i256 @hook_MINT_pow256(i256 %base, i256 %exp) {
568+
entry:
569+
; Special cases: if exp == 0 return 1, if base == 0 return 0
570+
%exp_is_zero = icmp eq i256 %exp, 0
571+
br i1 %exp_is_zero, label %exp_zero, label %check_base
572+
573+
exp_zero:
574+
ret i256 1
575+
576+
check_base:
577+
%base_is_zero = icmp eq i256 %base, 0
578+
br i1 %base_is_zero, label %base_zero, label %loop
579+
580+
base_zero:
581+
ret i256 0
582+
583+
; Initialize result = 1, current_base = base, current_exp = exp
584+
loop:
585+
%result = phi i256 [1, %check_base], [%next_result, %loop_body]
586+
%curr_base = phi i256 [%base, %check_base], [%next_base, %loop_body]
587+
%curr_exp = phi i256 [%exp, %check_base], [%next_exp, %loop_body]
588+
%exp_done = icmp eq i256 %curr_exp, 0
589+
br i1 %exp_done, label %done, label %loop_body
590+
591+
loop_body:
592+
; if (curr_exp & 1) result *= curr_base
593+
%exp_and_1 = and i256 %curr_exp, 1
594+
%is_odd = icmp ne i256 %exp_and_1, 0
595+
%mul_result = mul i256 %result, %curr_base
596+
%next_result = select i1 %is_odd, i256 %mul_result, i256 %result
597+
; curr_base *= curr_base
598+
%next_base = mul i256 %curr_base, %curr_base
599+
; curr_exp >>= 1
600+
%next_exp = lshr i256 %curr_exp, 1
601+
br label %loop
602+
603+
done:
604+
ret i256 %result
605+
}
606+
248607
define i64 @get_steps() {
249608
entry:
250609
%steps = load i64, ptr @steps

0 commit comments

Comments
 (0)