; Module : LLVM backend - comparison functions
; Description : Contains LLVM functions for integer/float comparison.
; Maintainers : Sascha Zinke, Tudor Soroceanu
; License : MIT
;
; These comparison functions are used by our LLVM backend and operate directly
; on the stack -- see stack.ll.
@err_numeric = external global [56 x i8]
@err_type = external global [14 x i8]
@err_zero = external global [18 x i8]
@popped = external global [13 x i8]
@true = external global [2 x i8]
@false = external global [2 x i8]
%stack_element = type opaque
%struct.stack_elem = type { i32, %union.anon }
%union.anon = type { i8* }
declare i8* @stack_element_get_data(%stack_element* %element)
declare i8 @stack_element_get_type(%stack_element*)
declare void @stack_element_unref(%stack_element* %element)
declare i32 @get_stack_elem(i8*, %struct.stack_elem*)
declare %stack_element* @push_string_ptr(i8* %str)
declare %stack_element* @push_string_cpy(i8* %str)
declare %stack_element* @pop_struct()
declare signext i32 @printf(i8*, ...)
declare void @push_float(double)
declare void @underflow_assert()
declare i32 @strcmp(i8*, i8*)
declare void @push_int(i64)
declare i8* @pop_string()
declare void @crash(i1)
declare i1 @list_equal(%stack_element*, %stack_element*)
@main.number_a = private unnamed_addr constant [4 x i8] c"abc\00"
@main.number_b = private unnamed_addr constant [4 x i8] c"adc\00"
define i32 @main_cmp() {
; push two numbers on the stack
%number0 = getelementptr [4 x i8]* @main.number_a, i64 0, i64 0
%number1 = getelementptr [4 x i8]* @main.number_b, i64 0, i64 0
call %stack_element* @push_string_cpy(i8* %number0)
call %stack_element* @push_string_cpy(i8* %number1)
call void @equal()
%result = call i8* @pop_string()
call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([13 x i8]*
@popped, i32 0, i32 0), i8* %result)
ret i32 0
}
;##############################################################################
; equal
;##############################################################################
; Check if the topmost stack elements are equal.
;
; Pushes true onto the stack if the elements are equal and false
; otherwise.
;
; Crashes the program on errors (prints an appropriate error message).
define void @equal() {
%struct_a = call %stack_element* @pop_struct()
%struct_b = call %stack_element* @pop_struct()
%are_equal = call i1 @do_equal(%stack_element* %struct_a, %stack_element* %struct_b)
br i1 %are_equal, label %push_true, label %push_false
push_true:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[2 x i8]* @true, i64 0, i64 0))
br label %done
push_false:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[2 x i8]* @false, i64 0, i64 0))
br label %done
done:
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_a)
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_b)
ret void
}
; Perform the actual equality check.
;
; Returns 1 if the stack elements are equal or 0 otherwise.
;
; Crashes the program on errors (prints an appropriate error message).
define i1 @do_equal(%stack_element* %struct_a, %stack_element* %struct_b) {
; return value of this function
%func_result = alloca i1, align 4
%new_elem_a = alloca %struct.stack_elem, align 8
%new_elem_b = alloca %struct.stack_elem, align 8
; Get data and type of first element.
%number_a = call i8*(%stack_element*)* @stack_element_get_data(
%stack_element* %struct_a)
%stack_type_a = call i8 @stack_element_get_type(%stack_element* %struct_a)
; Get data and type of second element.
%number_b = call i8*(%stack_element*)* @stack_element_get_data(
%stack_element* %struct_b)
%stack_type_b = call i8 @stack_element_get_type(%stack_element* %struct_b)
; The spec says that two elements of different types are always unequal.
%equal_stack_types = icmp eq i8 %stack_type_a, %stack_type_b
br i1 %equal_stack_types, label %check_stack_type, label %exit_with_false
check_stack_type:
; Same stack element type, so it does not matter which of the two types
; we use in the switch statement.
switch i8 %stack_type_a, label %exit_with_invalid_type
[
i8 0, label %get_stack_elem_a
i8 1, label %compare_lists
]
get_stack_elem_a:
; get type of number_a
%ret_a = call i32 @get_stack_elem(i8* %number_a, %struct.stack_elem* %new_elem_a)
%is_zero_a = icmp slt i32 %ret_a, 0
br i1 %is_zero_a, label %exit_with_numeric_failure, label %get_stack_elem_b
get_stack_elem_b:
; get type of number_b
%ret_b = call i32 @get_stack_elem(i8* %number_b, %struct.stack_elem* %new_elem_b)
%is_zero_b = icmp slt i32 %ret_b, 0
br i1 %is_zero_b, label %exit_with_numeric_failure, label %get_types
get_types:
; type of a
%type_a_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_a, i32 0, i32 0
%type_a = load i32* %type_a_ptr, align 4
%val_a_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_a, i32 0, i32 1
; type of b
%type_b_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_b, i32 0, i32 0
%type_b = load i32* %type_b_ptr, align 4
%val_b_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_b, i32 0, i32 1
switch i32 %type_a, label %exit_with_invalid_type [
i32 1, label %assume_b_int
i32 2, label %assume_b_float
i32 3, label %assume_b_string]
;##############################################################################
; list comparison
;##############################################################################
compare_lists:
%lists_are_equal = call i1 @list_equal(%stack_element* %struct_a, %stack_element* %struct_b)
br i1 %lists_are_equal, label %exit_with_true, label %exit_with_false
;##############################################################################
; integer comparison
;##############################################################################
assume_b_int:
; check whether it is 1 (aka INT).
%is_int_b = icmp eq i32 %type_b, 1
br i1 %is_int_b, label %cmp_int, label %exit_with_invalid_type
cmp_int:
; get new_elem_a.ival that contains the casted integer value
%ival_a_cast = bitcast %union.anon* %val_a_ptr to i32*
%ival_a = load i32* %ival_a_cast, align 4
; get new_elem_b.ival that contains the casted integer value
%ival_b_cast = bitcast %union.anon* %val_b_ptr to i32*
%ival_b = load i32* %ival_b_cast, align 4
; the actual comparison
%equal_int = icmp eq i32 %ival_a, %ival_b
br i1 %equal_int, label %exit_with_true, label %exit_with_false
;##############################################################################
; floating point comparison
;##############################################################################
assume_b_float:
; check whether it is 2 (aka FLOAT).
%is_float_b = icmp eq i32 %type_b, 2
br i1 %is_float_b, label %cmp_float, label %exit_with_invalid_type
cmp_float:
; get new_elem_a.fval that contains the float value
%fval_a_cast = bitcast %union.anon* %val_a_ptr to float*
%fval_a = load float* %fval_a_cast, align 4
; get new_elem_b.fval that contains the float value
%fval_b_cast = bitcast %union.anon* %val_b_ptr to float*
%fval_b = load float* %fval_b_cast, align 4
%equal_float = fcmp oeq float %fval_a, %fval_b
br i1 %equal_float, label %exit_with_true, label %exit_with_false
;##############################################################################
; string comparison
;##############################################################################
assume_b_string:
; check whether it is 3 (aka STRING).
%is_string_b = icmp eq i32 %type_b, 3
br i1 %is_string_b, label %cmp_str, label %exit_with_invalid_type
cmp_str:
%equal_string = call i32 @strcmp(i8* %number_a, i8* %number_b)
%is_equal = icmp eq i32 %equal_string, 0
br i1 %is_equal, label %exit_with_true, label %exit_with_false
exit_with_numeric_failure:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[56 x i8]* @err_numeric, i64 0, i64 0))
br label %exit_with_failure
exit_with_invalid_type:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[14 x i8]* @err_type, i64 0, i64 0))
br label %exit_with_failure
exit_with_failure:
call void @crash(i1 0)
ret i1 1
exit_with_true:
store i1 1, i1* %func_result
br label %exit
exit_with_false:
store i1 0, i1* %func_result
br label %exit
exit:
%result = load i1* %func_result
ret i1 %result
}
;##############################################################################
; greater
;##############################################################################
define i32 @greater(){
; return value of this function
%func_result = alloca i32, align 4
%new_elem_a = alloca %struct.stack_elem, align 8
%new_elem_b = alloca %struct.stack_elem, align 8
; get top of stack
call void @underflow_assert()
%struct_a = call %stack_element*()* @pop_struct()
%number_a = call i8*(%stack_element*)* @stack_element_get_data(
%stack_element* %struct_a)
; get second top of stack
call void @underflow_assert()
%struct_b = call %stack_element*()* @pop_struct()
%number_b = call i8*(%stack_element*)* @stack_element_get_data(
%stack_element* %struct_b)
; get type of number_a
%ret_a = call i32 @get_stack_elem(i8* %number_a, %struct.stack_elem* %new_elem_a)
%is_zero_a = icmp slt i32 %ret_a, 0
br i1 %is_zero_a, label %exit_with_numeric_failure, label %get_stack_elem_b
get_stack_elem_b:
; get type of number_b
%ret_b = call i32 @get_stack_elem(i8* %number_b, %struct.stack_elem* %new_elem_b)
%is_zero_b = icmp slt i32 %ret_b, 0
br i1 %is_zero_b, label %exit_with_numeric_failure, label %get_types
get_types:
; type of a
%type_a_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_a, i32 0, i32 0
%type_a = load i32* %type_a_ptr, align 4
%val_a_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_a, i32 0, i32 1
; type of b
%type_b_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_b, i32 0, i32 0
%type_b = load i32* %type_b_ptr, align 4
%val_b_ptr = getelementptr inbounds %struct.stack_elem* %new_elem_b, i32 0, i32 1
switch i32 %type_a, label %exit_with_invalid_type [
i32 1, label %assume_b_int
i32 2, label %assume_b_float]
;##############################################################################
; integer greater
;##############################################################################
assume_b_int:
; check whether it is 1 (aka INT).
%is_int_b = icmp eq i32 %type_b, 1
br i1 %is_int_b, label %cmp_int, label %exit_with_invalid_type
cmp_int:
; get new_elem_a.ival that contains the casted integer value
%ival_a_cast = bitcast %union.anon* %val_a_ptr to i32*
%ival_a = load i32* %ival_a_cast, align 4
; get new_elem_b.ival that contains the casted integer value
%ival_b_cast = bitcast %union.anon* %val_b_ptr to i32*
%ival_b = load i32* %ival_b_cast, align 4
; the actual comparison
%greater_int = icmp sgt i32 %ival_a, %ival_b
br i1 %greater_int, label %exit_with_true, label %exit_with_false
;##############################################################################
; floating point multiplication
;##############################################################################
assume_b_float:
; check whether it is 2 (aka FLOAT).
%is_float_b = icmp eq i32 %type_b, 2
br i1 %is_float_b, label %cmp_float, label %exit_with_invalid_type
cmp_float:
; get new_elem_a.fval that contains the float value
%fval_a_cast = bitcast %union.anon* %val_a_ptr to float*
%fval_a = load float* %fval_a_cast, align 4
; get new_elem_b.fval that contains the float value
%fval_b_cast = bitcast %union.anon* %val_b_ptr to float*
%fval_b = load float* %fval_b_cast, align 4
; prevent division by zero
%greater_float = fcmp ogt float %fval_a, %fval_b
br i1 %greater_float, label %exit_with_true, label %exit_with_false
exit_with_numeric_failure:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[56 x i8]* @err_numeric, i64 0, i64 0))
br label %exit_with_failure
exit_with_invalid_type:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[14 x i8]* @err_type, i64 0, i64 0))
br label %exit_with_failure
exit_with_failure:
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_a)
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_b)
call void @crash(i1 0)
br label %exit
exit_with_true:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[2 x i8]* @true, i64 0, i64 0))
br label %exit_with_success
exit_with_false:
call %stack_element* @push_string_cpy(i8* getelementptr inbounds(
[2 x i8]* @false, i64 0, i64 0))
br label %exit_with_success
exit_with_success:
store i32 0, i32* %func_result
br label %exit
exit:
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_a)
call void(%stack_element*)* @stack_element_unref(%stack_element* %struct_b)
%result = load i32* %func_result
ret i32 %result
}
; vim:ts=2 sw=2 et