; Module : LLVM backend - string functions
; Description : Contains LLVM functions for operations on strings (e. g. concatenation).
; Maintainers : Maximilian Claus
; License : MIT
;
; These functions are used by our LLVM backend and most of them operate
; directly on the stack -- see stack.ll.
@true = external global i8
@false = external global i8
@.str_err0 = unnamed_addr constant [41 x i8] c"Crash: strcut called with negative index\00"
@.str_err1 = unnamed_addr constant [58 x i8] c"Crash: strcut called with index larger than string length\00"
@strcut_neg_arg_err = global i8* getelementptr inbounds ([41 x i8]* @.str_err0, i64 0, i64 0), align 8
@strcut_too_large_arg_err = global i8* getelementptr inbounds ([58 x i8]* @.str_err1, i64 0, i64 0), align 8
; error handling
%FILE = type opaque
@stderr = external global %FILE*
declare signext i32 @fprintf(%FILE*, i8*, ...)
declare void @exit(i32 signext)
; stack functions
%stack_element = type opaque
declare void @underflow_assert()
declare %stack_element* @push_string_ptr(i8* %str)
declare %stack_element* @push_string_cpy(i8* %str)
declare void @push_int(i64)
declare %stack_element* @pop_struct()
declare void @stack_element_assert_type(%stack_element*, i8)
declare i8* @stack_element_get_data(%stack_element* %element)
declare i64 @stack_element_get_int_data(%stack_element* %element)
declare void @stack_element_unref(%stack_element* %element)
declare i8* @malloc(i16 zeroext) ; void *malloc(size_t) and size_t is 16 bits long (SIZE_MAX)
; TODO: free alloated space of input strings
define void @strapp() {
entry:
call void @underflow_assert()
%elem2 = call %stack_element*()* @pop_struct()
%str2 = call i8*(%stack_element*)* @stack_element_get_data(%stack_element* %elem2)
call void @underflow_assert()
%elem1 = call %stack_element*()* @pop_struct()
%str1 = call i8*(%stack_element*)* @stack_element_get_data(%stack_element* %elem1)
; compute length of input strings
%len_str1 = call i64(i8*)* @length(i8* %str1)
%len_str2 = call i64(i8*)* @length(i8* %str2)
; allocate space for result string
%len_result_1 = add i64 %len_str1, %len_str2
%len_result_2 = add i64 %len_result_1, 1
%len_result_3 = trunc i64 %len_result_2 to i16
%result = call i8* @malloc(i16 %len_result_3)
; copy first string into result
br label %loop1
loop1:
%i = phi i64 [0, %entry], [ %next_i, %loop1 ]
%next_i = add i64 %i, 1
%addr = getelementptr i8* %str1, i64 %i
%c = load i8* %addr
%result_addr = getelementptr i8* %result, i64 %i
store i8 %c, i8* %result_addr
%cond = icmp eq i8 %c, 0
br i1 %cond, label %finished, label %loop1
finished:
; copy second string into result
br label %loop2
loop2:
%j = phi i64 [0, %finished], [ %next_j, %loop2 ]
%next_j = add i64 %j, 1
%addr2 = getelementptr i8* %str2, i64 %j
%c2 = load i8* %addr2
%k = add i64 %j, %len_str1
%result_addr2 = getelementptr i8* %result, i64 %k
store i8 %c2, i8* %result_addr2
%cond2 = icmp eq i8 %c2, 0
br i1 %cond2, label %finished2, label %loop2
finished2:
call void(%stack_element*)* @stack_element_unref(%stack_element* %elem2)
call void(%stack_element*)* @stack_element_unref(%stack_element* %elem1)
call %stack_element* @push_string_ptr(i8* %result)
ret void
}
define i64 @length(i8* %str) {
entry:
br label %loop
loop:
%i = phi i64 [0, %entry ], [ %next_i, %loop ]
%next_i = add i64 %i, 1
%addr = getelementptr i8* %str, i64 %i
%c = load i8* %addr
%cond = icmp eq i8 %c, 0
br i1 %cond, label %finished, label %loop
finished:
ret i64 %i
}
define void @strlen() {
entry:
; pop string
call void @underflow_assert()
%elem = call %stack_element*()* @pop_struct()
%str = call i8*(%stack_element*)* @stack_element_get_data(%stack_element* %elem)
; compute length
%len = call i64(i8*)* @length(i8* %str)
; push length
call void(%stack_element*)* @stack_element_unref(%stack_element* %elem)
call void(i64)* @push_int(i64 %len)
ret void
}
define void @strcut() {
entry:
call void @underflow_assert()
%elem2 = call %stack_element*()* @pop_struct()
%indx = call i64(%stack_element*)* @stack_element_get_int_data(%stack_element* %elem2)
call void @underflow_assert()
%elem1 = call %stack_element*()* @pop_struct()
call void @stack_element_assert_type(%stack_element* %elem1, i8 0)
%str = call i8*(%stack_element*)* @stack_element_get_data(%stack_element* %elem1)
; allocate space for result strings
%len1_1 = add i64 %indx, 1
%len1 = trunc i64 %len1_1 to i16
%len_str = call i64(i8*)* @length(i8* %str)
%len2_1 = sub i64 %len_str, %indx
%len2_2 = add i64 %len2_1, 1
%len2 = trunc i64 %len2_2 to i16
%result1 = call i8* @malloc(i16 %len1)
%result2 = call i8* @malloc(i16 %len2)
; check whether index argument is within bounds
%stderr = load %FILE** @stderr
%err0 = icmp slt i64 %indx, 0
br i1 %err0, label %neg_arg, label %continue_check
neg_arg:
%err_msg0 = load i8** @strcut_neg_arg_err
call i32(%FILE*, i8*, ...)* @fprintf(%FILE* %stderr, i8* %err_msg0)
call void @exit(i32 1)
ret void
continue_check:
%err1 = icmp sgt i64 %indx, %len_str
br i1 %err1, label %too_large_arg, label %loop1
too_large_arg:
%err_msg1 = load i8** @strcut_too_large_arg_err
call i32(%FILE*, i8*, ...)* @fprintf(%FILE* %stderr, i8* %err_msg1)
call void @exit(i32 1)
ret void
; fill result1 string
loop1:
%i = phi i64 [0, %continue_check], [ %next_i, %loop1 ]
%next_i = add i64 %i, 1
%addr = getelementptr i8* %str, i64 %i
%c = load i8* %addr
%result_addr = getelementptr i8* %result1, i64 %i
store i8 %c, i8* %result_addr
%cond = icmp eq i64 %i, %indx
br i1 %cond, label %finished, label %loop1
finished:
%end_addr = getelementptr i8* %result1, i64 %indx
store i8 0, i8* %end_addr
; fill result2 string
br label %loop2
loop2:
%j = phi i64 [0, %finished], [ %next_j, %loop2 ]
%next_j = add i64 %j, 1
%k = add i64 %j, %indx
%addr2 = getelementptr i8* %str, i64 %k
%c2 = load i8* %addr2
%result_addr2 = getelementptr i8* %result2, i64 %j
store i8 %c2, i8* %result_addr2
%cond2 = icmp eq i8 %c2, 0
br i1 %cond2, label %finished2, label %loop2
finished2:
call void(%stack_element*)* @stack_element_unref(%stack_element* %elem2)
call void(%stack_element*)* @stack_element_unref(%stack_element* %elem1)
call %stack_element* @push_string_ptr(i8* %result2)
call %stack_element* @push_string_ptr(i8* %result1)
ret void
}
; vim:sw=2 ts=2 et