Make typeid switches always use subtype matching. Update seeder mixing.

This commit is contained in:
Christoffer Lerno
2023-08-29 22:26:25 +02:00
committed by Christoffer Lerno
parent 7aca8a02cb
commit eac19814e1
14 changed files with 595 additions and 225 deletions

View File

@@ -33,11 +33,11 @@ else()
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined -fno-exceptions")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fno-exceptions")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined,address -fno-exceptions")
endif()
endif()

View File

@@ -74,6 +74,24 @@ macro any_to_int(any v, $Type)
}
}
fn bool typeid.is_subtype_of(self, typeid other)
{
while (self != void.typeid)
{
if (self == other) return true;
self = self.parentof;
}
return false;
}
macro bool is_subtype_of($Type, $OtherType)
{
var $typeid = $Type.typeid;
$switch ($Type)
$case $OtherType: return true;
$default: return false;
$endswitch
}
macro bool is_numerical($Type)
{
var $kind = $Type.kindof;

View File

@@ -27,6 +27,7 @@ fn void seeder(char[] input, char[] out_buffer)
{
ulong[] words = tmalloc(ulong, (out_chars + 7) / 8);
words[..] = ODD_PHI64;
usz words_len_2 = words.len * 2;
// Add word at a time
for (usz i = 0; i < input.len / 8; i++)
@@ -47,7 +48,7 @@ fn void seeder(char[] input, char[] out_buffer)
}
// Mix between words
for (isz i = words.len * 2 - 1; i >= 0; i--)
for (isz i = words_len_2 - 1; i >= 0; i--)
{
isz j = i % words.len;
words[j] -= words[(i + 1) % words.len] * MUL_LCG64;
@@ -55,7 +56,7 @@ fn void seeder(char[] input, char[] out_buffer)
}
// Mix within words
for (usz i = 0; i < 2; i++)
for (usz i = 0; i < words_len_2; i++)
{
usz j = i % words.len;
words[j] *= MUL_MCG64;

View File

@@ -3,6 +3,7 @@
## 0.5.0 Change List
### Changes / improvements
- Subtype matching in type switches.
- Added parentof typeid property.
- Slice assignment is expanded.
- Strong optional handling requirements.

View File

@@ -3734,13 +3734,33 @@ static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs,
void llvm_emit_lhs_is_subtype(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs)
{
Type *canonical_typeid = type_lowering(type_typeid);
llvm_value_rvalue(c, lhs);
llvm_value_rvalue(c, rhs);
LLVMValueRef switch_val = lhs->value;
LLVMBasicBlockRef start_block = c->current_block;
LLVMBasicBlockRef retry_block = llvm_basic_block_new(c, "check_subtype");
LLVMBasicBlockRef result_block = llvm_basic_block_new(c, "result_block");
LLVMBasicBlockRef parent_type_block = llvm_basic_block_new(c, "parent_type_block");
llvm_emit_br(c, retry_block);
llvm_emit_comp(c, result, lhs, rhs, BINARYOP_EQ);
LLVMBasicBlockRef next_block = llvm_basic_block_new(c, "next_block");
TODO
llvm_emit_block(c, retry_block);
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->typeid_type, "");
BEValue cond;
llvm_emit_int_comp_raw(c, &cond, canonical_typeid, canonical_typeid, switch_val, phi, BINARYOP_EQ);
llvm_emit_cond_br(c, &cond, result_block, parent_type_block);
llvm_emit_block(c, parent_type_block);
LLVMValueRef introspect_ptr = LLVMBuildIntToPtr(c->builder, phi, c->ptr_type, "");
AlignSize alignment;
LLVMValueRef parent = llvm_emit_struct_gep_raw(c, introspect_ptr, c->introspect_type, INTROSPECT_INDEX_PARENTOF,
type_abi_alignment(type_voidptr), &alignment);
LLVMValueRef parent_value = llvm_load(c, c->typeid_type, parent, alignment, "typeid.parent");
LLVMValueRef is_zero = LLVMBuildICmp(c->builder, LLVMIntEQ, parent_value, LLVMConstNull(c->typeid_type), "");
llvm_emit_cond_br_raw(c, is_zero, result_block, retry_block);
llvm_set_phi(phi, rhs->value, start_block, parent_value, parent_type_block);
llvm_emit_block(c, result_block);
LLVMValueRef phi2 = LLVMBuildPhi(c->builder, c->bool_type, "");
llvm_set_phi(phi2, LLVMConstNull(c->bool_type), parent_type_block, LLVMConstAllOnes(c->bool_type), retry_block);
llvm_value_set(result, phi2, type_bool);
}
void llvm_emit_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op)

View File

@@ -4,7 +4,7 @@
#include "llvm_codegen_internal.h"
static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast);
static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast, bool is_typeid);
// Emit a regular compound statement.
void llvm_emit_compound_stmt(GenContext *c, Ast *ast)
@@ -332,7 +332,7 @@ static void llvm_emit_if_stmt(GenContext *c, Ast *ast)
llvm_emit_cond_br(c, &comp, then_block, else_block);
llvm_emit_br(c, then_block);
llvm_emit_block(c, then_block);
llvm_emit_switch_body(c, &be_value, then_body);
llvm_emit_switch_body(c, &be_value, then_body, false);
llvm_emit_br(c, exit_block);
goto EMIT_ELSE;
}
@@ -615,7 +615,8 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
Ast **cases,
Ast *default_case,
BEValue *switch_value,
LLVMBasicBlockRef exit_block)
LLVMBasicBlockRef exit_block,
bool is_type_switch)
{
LLVMBasicBlockRef next = NULL;
VECEACH(cases, i)
@@ -631,6 +632,7 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
Expr *to_expr = exprptrzero(case_stmt->case_stmt.to_expr);
if (to_expr)
{
assert(!is_type_switch);
BEValue to_value;
llvm_emit_expr(c, &to_value, to_expr);
llvm_value_rvalue(c, &to_value);
@@ -642,11 +644,11 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
}
else
{
/*if (expr->type == type_typeid)
if (is_type_switch)
{
llvm_emit_lhs_is_subtype(c, &equals, &be_value, switch_value);
}
else*/
else
{
llvm_emit_comp(c, &equals, &be_value, switch_value, BINARYOP_EQ);
}
@@ -752,11 +754,10 @@ static void llvm_emit_switch_jump_table(GenContext *c,
#endif
}
static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast)
static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *switch_ast, bool is_typeid)
{
bool is_if_chain = switch_ast->switch_stmt.flow.if_chain;
Type *switch_type = switch_ast->ast_kind == AST_IF_CATCH_SWITCH_STMT ? type_lowering(type_anyfault) : switch_value->type;
Ast **cases = switch_ast->switch_stmt.cases;
ArraySize case_count = vec_size(cases);
if (!case_count)
@@ -821,9 +822,10 @@ static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *swi
if (is_if_chain)
{
llvm_emit_switch_body_if_chain(c, cases, default_case, &switch_current_val, exit_block);
llvm_emit_switch_body_if_chain(c, cases, default_case, &switch_current_val, exit_block, is_typeid);
return;
}
assert(!is_typeid);
c->current_block = NULL;
LLVMValueRef switch_stmt = LLVMBuildSwitch(c->builder, switch_current_val.value, default_case ? default_case->case_stmt.backend_block : exit_block, case_count);
@@ -875,6 +877,7 @@ void llvm_emit_switch(GenContext *c, Ast *ast)
{
BEValue switch_value;
Expr *expr = exprptrzero(ast->switch_stmt.cond);
bool is_typeid = expr && expr->type->canonical == type_typeid;
if (expr)
{
// Regular switch
@@ -885,7 +888,7 @@ void llvm_emit_switch(GenContext *c, Ast *ast)
// Match switch, so set the value to true
llvm_value_set(&switch_value, llvm_const_int(c, type_bool, 1), type_bool);
}
llvm_emit_switch_body(c, &switch_value, ast);
llvm_emit_switch_body(c, &switch_value, ast, is_typeid);
}

View File

@@ -2438,7 +2438,17 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
goto FAILED;
}
}
if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
if (is_type)
{
assert(const_expr == const_to_expr);
Type *switch_type = switch_expr_const->typeid;
Type *case_type = const_expr->typeid;
if (matched_case > i && type_is_subtype(case_type->canonical, switch_type->canonical))
{
matched_case = (int)i;
}
}
else if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
{
matched_case = (int)i;
}

View File

@@ -1617,27 +1617,19 @@ Type *type_find_parent_type(Type *type)
* Check if a type is contained in another type.
*
* @param type canonical type
* @param possible_subtype canonical type
* @param possible_subtype is this a subtype of the former, i.e. type is the parent.
* @return true if it is a subtype
*/
bool type_is_subtype(Type *type, Type *possible_subtype)
{
assert(type == type->canonical && possible_subtype == possible_subtype->canonical);
while (1)
assert(type == type->canonical);
while (possible_subtype)
{
possible_subtype = possible_subtype->canonical;
if (type == possible_subtype) return true;
if (type->type_kind != possible_subtype->type_kind) return false;
if (type->type_kind != TYPE_STRUCT) return false;
if (!possible_subtype->decl->strukt.members) return false;
Decl *first_element = possible_subtype->decl->strukt.members[0];
if (first_element->decl_kind != DECL_VAR) return false;
possible_subtype = first_element->type->canonical;
possible_subtype = type_find_parent_type(possible_subtype);
}
return false;
}
Type *type_from_token(TokenType type)

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.622"
#define COMPILER_VERSION "0.4.623"

View File

@@ -79,8 +79,8 @@ entry:
%a = alloca i32, align 4
%b = alloca i32, align 4
%zy = alloca i32, align 4
%switch17 = alloca i32, align 4
%switch27 = alloca i8, align 1
%switch25 = alloca i32, align 4
%switch35 = alloca i8, align 1
store i64 ptrtoint (ptr @"mymodule.ByeErr$BAR" to i64), ptr %x.f, align 8
store i64 ptrtoint (ptr @"$ct.int" to i64), ptr %z, align 8
br label %testblock
@@ -142,104 +142,157 @@ if.exit: ; preds = %switch.exit, %end_b
switch.entry5: ; preds = %if.exit
%4 = load i64, ptr %switch4, align 8
%eq6 = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq6, label %switch.case7, label %next_if8
br label %check_subtype
switch.case7: ; preds = %switch.entry5
check_subtype: ; preds = %parent_type_block, %switch.entry5
%5 = phi i64 [ %4, %switch.entry5 ], [ %typeid.parent, %parent_type_block ]
%eq6 = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq6, label %result_block, label %parent_type_block
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case7, label %next_if8
switch.case7: ; preds = %result_block
call void (ptr, ...) @printf(ptr @.str.5)
br label %switch.exit16
br label %switch.exit24
next_if8: ; preds = %switch.entry5
%eq9 = icmp eq i64 ptrtoint (ptr @"$ct.bool" to i64), %4
br i1 %eq9, label %switch.case10, label %next_if11
next_if8: ; preds = %result_block
br label %check_subtype9
switch.case10: ; preds = %next_if8
br label %switch.case13
check_subtype9: ; preds = %parent_type_block11, %next_if8
%10 = phi i64 [ %4, %next_if8 ], [ %typeid.parent12, %parent_type_block11 ]
%eq10 = icmp eq i64 ptrtoint (ptr @"$ct.bool" to i64), %10
br i1 %eq10, label %result_block13, label %parent_type_block11
next_if11: ; preds = %next_if8
%eq12 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq12, label %switch.case13, label %next_if14
parent_type_block11: ; preds = %check_subtype9
%11 = inttoptr i64 %10 to ptr
%12 = getelementptr inbounds %.introspect, ptr %11, i32 0, i32 1
%typeid.parent12 = load i64, ptr %12, align 8
%13 = icmp eq i64 %typeid.parent12, 0
br i1 %13, label %result_block13, label %check_subtype9
switch.case13: ; preds = %next_if11, %switch.case10
br label %switch.default15
result_block13: ; preds = %parent_type_block11, %check_subtype9
%14 = phi i1 [ false, %parent_type_block11 ], [ true, %check_subtype9 ]
br i1 %14, label %switch.case14, label %next_if15
next_if14: ; preds = %next_if11
br label %switch.default15
switch.case14: ; preds = %result_block13
br label %switch.case21
switch.default15: ; preds = %next_if14, %switch.case13
next_if15: ; preds = %result_block13
br label %check_subtype16
check_subtype16: ; preds = %parent_type_block18, %next_if15
%15 = phi i64 [ %4, %next_if15 ], [ %typeid.parent19, %parent_type_block18 ]
%eq17 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %15
br i1 %eq17, label %result_block20, label %parent_type_block18
parent_type_block18: ; preds = %check_subtype16
%16 = inttoptr i64 %15 to ptr
%17 = getelementptr inbounds %.introspect, ptr %16, i32 0, i32 1
%typeid.parent19 = load i64, ptr %17, align 8
%18 = icmp eq i64 %typeid.parent19, 0
br i1 %18, label %result_block20, label %check_subtype16
result_block20: ; preds = %parent_type_block18, %check_subtype16
%19 = phi i1 [ false, %parent_type_block18 ], [ true, %check_subtype16 ]
br i1 %19, label %switch.case21, label %next_if22
switch.case21: ; preds = %result_block20, %switch.case14
br label %switch.default23
next_if22: ; preds = %result_block20
br label %switch.default23
switch.default23: ; preds = %next_if22, %switch.case21
call void (ptr, ...) @printf(ptr @.str.6)
br label %switch.exit16
br label %switch.exit24
switch.exit16: ; preds = %switch.default15, %switch.case7
switch.exit24: ; preds = %switch.default23, %switch.case7
store i32 1, ptr %a, align 4
store i32 2, ptr %b, align 4
%5 = load i32, ptr %b, align 4
%6 = load i32, ptr %a, align 4
%add = add i32 %5, %6
%20 = load i32, ptr %b, align 4
%21 = load i32, ptr %a, align 4
%add = add i32 %20, %21
store i32 %add, ptr %zy, align 4
%7 = load i32, ptr %zy, align 4
store i32 %7, ptr %switch17, align 4
br label %switch.entry18
%22 = load i32, ptr %zy, align 4
store i32 %22, ptr %switch25, align 4
br label %switch.entry26
switch.entry18: ; preds = %switch.exit16
%8 = load i32, ptr %switch17, align 4
%9 = load i32, ptr %a, align 4
%eq19 = icmp eq i32 %9, %8
br i1 %eq19, label %switch.case20, label %next_if21
switch.entry26: ; preds = %switch.exit24
%23 = load i32, ptr %switch25, align 4
%24 = load i32, ptr %a, align 4
%eq27 = icmp eq i32 %24, %23
br i1 %eq27, label %switch.case28, label %next_if29
switch.case20: ; preds = %switch.entry18
switch.case28: ; preds = %switch.entry26
call void (ptr, ...) @printf(ptr @.str.7)
br label %switch.exit26
br label %switch.exit34
next_if21: ; preds = %switch.entry18
%10 = load i32, ptr %b, align 4
%eq22 = icmp eq i32 %10, %8
br i1 %eq22, label %switch.case23, label %next_if24
next_if29: ; preds = %switch.entry26
%25 = load i32, ptr %b, align 4
%eq30 = icmp eq i32 %25, %23
br i1 %eq30, label %switch.case31, label %next_if32
switch.case23: ; preds = %next_if21
switch.case31: ; preds = %next_if29
call void (ptr, ...) @printf(ptr @.str.8)
br label %switch.exit26
br label %switch.exit34
next_if24: ; preds = %next_if21
br label %switch.default25
next_if32: ; preds = %next_if29
br label %switch.default33
switch.default25: ; preds = %next_if24
switch.default33: ; preds = %next_if32
call void (ptr, ...) @printf(ptr @.str.9)
br label %switch.exit26
br label %switch.exit34
switch.exit26: ; preds = %switch.default25, %switch.case23, %switch.case20
store i8 1, ptr %switch27, align 1
br label %switch.entry28
switch.exit34: ; preds = %switch.default33, %switch.case31, %switch.case28
store i8 1, ptr %switch35, align 1
br label %switch.entry36
switch.entry28: ; preds = %switch.exit26
%11 = load i8, ptr %switch27, align 1
%12 = trunc i8 %11 to i1
%13 = load i32, ptr %a, align 4
%lt = icmp slt i32 %13, 0
%eq29 = icmp eq i1 %lt, %12
br i1 %eq29, label %switch.case30, label %next_if31
switch.entry36: ; preds = %switch.exit34
%26 = load i8, ptr %switch35, align 1
%27 = trunc i8 %26 to i1
%28 = load i32, ptr %a, align 4
%lt = icmp slt i32 %28, 0
%eq37 = icmp eq i1 %lt, %27
br i1 %eq37, label %switch.case38, label %next_if39
switch.case30: ; preds = %switch.entry28
switch.case38: ; preds = %switch.entry36
call void (ptr, ...) @printf(ptr @.str.10)
br label %switch.exit37
br label %switch.exit45
next_if31: ; preds = %switch.entry28
%14 = load i32, ptr %a, align 4
%eq32 = icmp eq i32 %14, 1
%eq33 = icmp eq i1 %eq32, %12
br i1 %eq33, label %switch.case34, label %next_if35
next_if39: ; preds = %switch.entry36
%29 = load i32, ptr %a, align 4
%eq40 = icmp eq i32 %29, 1
%eq41 = icmp eq i1 %eq40, %27
br i1 %eq41, label %switch.case42, label %next_if43
switch.case34: ; preds = %next_if31
switch.case42: ; preds = %next_if39
call void (ptr, ...) @printf(ptr @.str.11)
br label %switch.exit37
br label %switch.exit45
next_if35: ; preds = %next_if31
br label %switch.default36
next_if43: ; preds = %next_if39
br label %switch.default44
switch.default36: ; preds = %next_if35
switch.default44: ; preds = %next_if43
call void (ptr, ...) @printf(ptr @.str.12)
br label %switch.exit37
br label %switch.exit45
switch.exit37: ; preds = %switch.default36, %switch.case34, %switch.case30
switch.exit45: ; preds = %switch.default44, %switch.case42, %switch.case38
ret void
}
; Function Attrs: nounwind
define void @mymodule.main() #0 {
entry:
call void @mymodule.test()
call void (ptr, ...) @printf(ptr @.str.13)
ret void
}

View File

@@ -67,7 +67,7 @@ entry:
%z = alloca %any, align 8
%switch = alloca i64, align 8
%z1 = alloca ptr, align 8
%z4 = alloca ptr, align 8
%z8 = alloca ptr, align 8
store i64 %0, ptr %z, align 8
%ptroffset = getelementptr inbounds ptr, ptr %z, i64 1
store ptr %1, ptr %ptroffset, align 8
@@ -78,42 +78,73 @@ entry:
switch.entry: ; preds = %entry
%4 = load i64, ptr %switch, align 8
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq, label %switch.case, label %next_if
br label %check_subtype
switch.case: ; preds = %switch.entry
%5 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%6 = load ptr, ptr %5, align 8
store ptr %6, ptr %z1, align 8
%7 = load ptr, ptr %z1, align 8
%8 = load i32, ptr %7, align 4
call void (ptr, ...) @printf(ptr @.str, i32 %8)
check_subtype: ; preds = %parent_type_block, %switch.entry
%5 = phi i64 [ %4, %switch.entry ], [ %typeid.parent, %parent_type_block ]
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq, label %result_block, label %parent_type_block
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case, label %next_if
switch.case: ; preds = %result_block
%10 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
store ptr %11, ptr %z1, align 8
%12 = load ptr, ptr %z1, align 8
%13 = load i32, ptr %12, align 4
call void (ptr, ...) @printf(ptr @.str, i32 %13)
br label %switch.exit
next_if: ; preds = %switch.entry
%eq2 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq2, label %switch.case3, label %next_if5
next_if: ; preds = %result_block
br label %check_subtype2
switch.case3: ; preds = %next_if
%9 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%10 = load ptr, ptr %9, align 8
store ptr %10, ptr %z4, align 8
%11 = load ptr, ptr %z4, align 8
%12 = load double, ptr %11, align 8
call void (ptr, ...) @printf(ptr @.str.1, double %12)
check_subtype2: ; preds = %parent_type_block4, %next_if
%14 = phi i64 [ %4, %next_if ], [ %typeid.parent5, %parent_type_block4 ]
%eq3 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %14
br i1 %eq3, label %result_block6, label %parent_type_block4
parent_type_block4: ; preds = %check_subtype2
%15 = inttoptr i64 %14 to ptr
%16 = getelementptr inbounds %.introspect, ptr %15, i32 0, i32 1
%typeid.parent5 = load i64, ptr %16, align 8
%17 = icmp eq i64 %typeid.parent5, 0
br i1 %17, label %result_block6, label %check_subtype2
result_block6: ; preds = %parent_type_block4, %check_subtype2
%18 = phi i1 [ false, %parent_type_block4 ], [ true, %check_subtype2 ]
br i1 %18, label %switch.case7, label %next_if9
switch.case7: ; preds = %result_block6
%19 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%20 = load ptr, ptr %19, align 8
store ptr %20, ptr %z8, align 8
%21 = load ptr, ptr %z8, align 8
%22 = load double, ptr %21, align 8
call void (ptr, ...) @printf(ptr @.str.1, double %22)
br label %switch.exit
next_if5: ; preds = %next_if
next_if9: ; preds = %result_block6
br label %switch.default
switch.default: ; preds = %next_if5
switch.default: ; preds = %next_if9
call void (ptr, ...) @printf(ptr @.str.2)
br label %switch.exit
switch.exit: ; preds = %switch.default, %switch.case3, %switch.case
switch.exit: ; preds = %switch.default, %switch.case7, %switch.case
ret void
}
; Function Attrs: nounwind
define void @foo.test2(i64 %0, ptr %1) #0 {
entry:
%y = alloca %any, align 8
@@ -121,7 +152,7 @@ entry:
%switch = alloca i64, align 8
%z = alloca ptr, align 8
%taddr = alloca i32, align 4
%z3 = alloca ptr, align 8
%z7 = alloca ptr, align 8
store i64 %0, ptr %y, align 8
%ptroffset = getelementptr inbounds ptr, ptr %y, i64 1
store ptr %1, ptr %ptroffset, align 8
@@ -133,43 +164,73 @@ entry:
switch.entry: ; preds = %entry
%4 = load i64, ptr %switch, align 8
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq, label %switch.case, label %next_if
br label %check_subtype
switch.case: ; preds = %switch.entry
%5 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%6 = load ptr, ptr %5, align 8
store ptr %6, ptr %z, align 8
check_subtype: ; preds = %parent_type_block, %switch.entry
%5 = phi i64 [ %4, %switch.entry ], [ %typeid.parent, %parent_type_block ]
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq, label %result_block, label %parent_type_block
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case, label %next_if
switch.case: ; preds = %result_block
%10 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
store ptr %11, ptr %z, align 8
store i32 12, ptr %taddr, align 4
%7 = insertvalue %any undef, ptr %taddr, 0
%8 = insertvalue %any %7, i64 ptrtoint (ptr @"$ct.int" to i64), 1
store %any %8, ptr %y, align 8
%9 = load ptr, ptr %z, align 8
%10 = load i32, ptr %9, align 4
call void (ptr, ...) @printf(ptr @.str.3, i32 %10)
%12 = insertvalue %any undef, ptr %taddr, 0
%13 = insertvalue %any %12, i64 ptrtoint (ptr @"$ct.int" to i64), 1
store %any %13, ptr %y, align 8
%14 = load ptr, ptr %z, align 8
%15 = load i32, ptr %14, align 4
call void (ptr, ...) @printf(ptr @.str.3, i32 %15)
br label %switch.exit
next_if: ; preds = %switch.entry
%eq1 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq1, label %switch.case2, label %next_if4
next_if: ; preds = %result_block
br label %check_subtype1
switch.case2: ; preds = %next_if
%11 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%12 = load ptr, ptr %11, align 8
store ptr %12, ptr %z3, align 8
%13 = load ptr, ptr %z3, align 8
%14 = load double, ptr %13, align 8
call void (ptr, ...) @printf(ptr @.str.4, double %14)
check_subtype1: ; preds = %parent_type_block3, %next_if
%16 = phi i64 [ %4, %next_if ], [ %typeid.parent4, %parent_type_block3 ]
%eq2 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %16
br i1 %eq2, label %result_block5, label %parent_type_block3
parent_type_block3: ; preds = %check_subtype1
%17 = inttoptr i64 %16 to ptr
%18 = getelementptr inbounds %.introspect, ptr %17, i32 0, i32 1
%typeid.parent4 = load i64, ptr %18, align 8
%19 = icmp eq i64 %typeid.parent4, 0
br i1 %19, label %result_block5, label %check_subtype1
result_block5: ; preds = %parent_type_block3, %check_subtype1
%20 = phi i1 [ false, %parent_type_block3 ], [ true, %check_subtype1 ]
br i1 %20, label %switch.case6, label %next_if8
switch.case6: ; preds = %result_block5
%21 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%22 = load ptr, ptr %21, align 8
store ptr %22, ptr %z7, align 8
%23 = load ptr, ptr %z7, align 8
%24 = load double, ptr %23, align 8
call void (ptr, ...) @printf(ptr @.str.4, double %24)
br label %switch.exit
next_if4: ; preds = %next_if
next_if8: ; preds = %result_block5
br label %switch.default
switch.default: ; preds = %next_if4
switch.default: ; preds = %next_if8
call void (ptr, ...) @printf(ptr @.str.5)
br label %switch.exit
switch.exit: ; preds = %switch.default, %switch.case2, %switch.case
switch.exit: ; preds = %switch.default, %switch.case6, %switch.case
ret void
}
@@ -179,7 +240,7 @@ entry:
%.anon = alloca %any, align 8
%switch = alloca i64, align 8
%z = alloca i32, align 4
%z3 = alloca double, align 8
%z7 = alloca double, align 8
store i64 %0, ptr %y, align 8
%ptroffset = getelementptr inbounds ptr, ptr %y, i64 1
store ptr %1, ptr %ptroffset, align 8
@@ -191,39 +252,69 @@ entry:
switch.entry: ; preds = %entry
%4 = load i64, ptr %switch, align 8
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq, label %switch.case, label %next_if
br label %check_subtype
switch.case: ; preds = %switch.entry
%5 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%6 = load ptr, ptr %5, align 8
%7 = load i32, ptr %6, align 4
store i32 %7, ptr %z, align 4
%8 = load i32, ptr %z, align 4
call void (ptr, ...) @printf(ptr @.str.6, i32 %8)
check_subtype: ; preds = %parent_type_block, %switch.entry
%5 = phi i64 [ %4, %switch.entry ], [ %typeid.parent, %parent_type_block ]
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq, label %result_block, label %parent_type_block
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case, label %next_if
switch.case: ; preds = %result_block
%10 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
%12 = load i32, ptr %11, align 4
store i32 %12, ptr %z, align 4
%13 = load i32, ptr %z, align 4
call void (ptr, ...) @printf(ptr @.str.6, i32 %13)
br label %switch.exit
next_if: ; preds = %switch.entry
%eq1 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq1, label %switch.case2, label %next_if4
next_if: ; preds = %result_block
br label %check_subtype1
switch.case2: ; preds = %next_if
%9 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%10 = load ptr, ptr %9, align 8
%11 = load double, ptr %10, align 8
store double %11, ptr %z3, align 8
%12 = load double, ptr %z3, align 8
call void (ptr, ...) @printf(ptr @.str.7, double %12)
check_subtype1: ; preds = %parent_type_block3, %next_if
%14 = phi i64 [ %4, %next_if ], [ %typeid.parent4, %parent_type_block3 ]
%eq2 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %14
br i1 %eq2, label %result_block5, label %parent_type_block3
parent_type_block3: ; preds = %check_subtype1
%15 = inttoptr i64 %14 to ptr
%16 = getelementptr inbounds %.introspect, ptr %15, i32 0, i32 1
%typeid.parent4 = load i64, ptr %16, align 8
%17 = icmp eq i64 %typeid.parent4, 0
br i1 %17, label %result_block5, label %check_subtype1
result_block5: ; preds = %parent_type_block3, %check_subtype1
%18 = phi i1 [ false, %parent_type_block3 ], [ true, %check_subtype1 ]
br i1 %18, label %switch.case6, label %next_if8
switch.case6: ; preds = %result_block5
%19 = getelementptr inbounds %any, ptr %.anon, i32 0, i32 0
%20 = load ptr, ptr %19, align 8
%21 = load double, ptr %20, align 8
store double %21, ptr %z7, align 8
%22 = load double, ptr %z7, align 8
call void (ptr, ...) @printf(ptr @.str.7, double %22)
br label %switch.exit
next_if4: ; preds = %next_if
next_if8: ; preds = %result_block5
br label %switch.default
switch.default: ; preds = %next_if4
switch.default: ; preds = %next_if8
call void (ptr, ...) @printf(ptr @.str.8)
br label %switch.exit
switch.exit: ; preds = %switch.default, %switch.case2, %switch.case
switch.exit: ; preds = %switch.default, %switch.case6, %switch.case
ret void
}

View File

@@ -39,7 +39,7 @@ entry:
%z = alloca %any, align 8
%switch = alloca i64, align 8
%z1 = alloca ptr, align 8
%z4 = alloca ptr, align 8
%z8 = alloca ptr, align 8
store i64 %0, ptr %z, align 8
%ptroffset = getelementptr inbounds ptr, ptr %z, i64 1
store ptr %1, ptr %ptroffset, align 8
@@ -50,51 +50,81 @@ entry:
switch.entry: ; preds = %entry
%4 = load i64, ptr %switch, align 8
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq, label %switch.case, label %next_if
br label %check_subtype
switch.case: ; preds = %switch.entry
%5 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%6 = load ptr, ptr %5, align 8
store ptr %6, ptr %z1, align 8
%7 = load ptr, ptr %z1, align 8
%8 = load i32, ptr %7, align 4
call void (ptr, ...) @printf(ptr @.str, i32 %8)
%9 = load ptr, ptr %z1, align 8
store i32 3, ptr %9, align 4
br label %switch.exit
check_subtype: ; preds = %parent_type_block, %switch.entry
%5 = phi i64 [ %4, %switch.entry ], [ %typeid.parent, %parent_type_block ]
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq, label %result_block, label %parent_type_block
next_if: ; preds = %switch.entry
%eq2 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq2, label %switch.case3, label %next_if5
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
switch.case3: ; preds = %next_if
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case, label %next_if
switch.case: ; preds = %result_block
%10 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
store ptr %11, ptr %z4, align 8
%12 = load ptr, ptr %z4, align 8
%13 = load double, ptr %12, align 8
call void (ptr, ...) @printf(ptr @.str.1, double %13)
store ptr %11, ptr %z1, align 8
%12 = load ptr, ptr %z1, align 8
%13 = load i32, ptr %12, align 4
call void (ptr, ...) @printf(ptr @.str, i32 %13)
%14 = load ptr, ptr %z1, align 8
store i32 3, ptr %14, align 4
br label %switch.exit
next_if5: ; preds = %next_if
next_if: ; preds = %result_block
br label %check_subtype2
check_subtype2: ; preds = %parent_type_block4, %next_if
%15 = phi i64 [ %4, %next_if ], [ %typeid.parent5, %parent_type_block4 ]
%eq3 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %15
br i1 %eq3, label %result_block6, label %parent_type_block4
parent_type_block4: ; preds = %check_subtype2
%16 = inttoptr i64 %15 to ptr
%17 = getelementptr inbounds %.introspect, ptr %16, i32 0, i32 1
%typeid.parent5 = load i64, ptr %17, align 8
%18 = icmp eq i64 %typeid.parent5, 0
br i1 %18, label %result_block6, label %check_subtype2
result_block6: ; preds = %parent_type_block4, %check_subtype2
%19 = phi i1 [ false, %parent_type_block4 ], [ true, %check_subtype2 ]
br i1 %19, label %switch.case7, label %next_if9
switch.case7: ; preds = %result_block6
%20 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%21 = load ptr, ptr %20, align 8
store ptr %21, ptr %z8, align 8
%22 = load ptr, ptr %z8, align 8
%23 = load double, ptr %22, align 8
call void (ptr, ...) @printf(ptr @.str.1, double %23)
br label %switch.exit
next_if9: ; preds = %result_block6
br label %switch.default
switch.default: ; preds = %next_if5
switch.default: ; preds = %next_if9
call void (ptr, ...) @printf(ptr @.str.2)
br label %switch.exit
switch.exit: ; preds = %switch.default, %switch.case3, %switch.case
%14 = getelementptr inbounds %any, ptr %z, i32 0, i32 1
%15 = load i64, ptr %14, align 8
%eq6 = icmp eq i64 %15, ptrtoint (ptr @"$ct.int" to i64)
br i1 %eq6, label %if.then, label %if.exit
switch.exit: ; preds = %switch.default, %switch.case7, %switch.case
%24 = getelementptr inbounds %any, ptr %z, i32 0, i32 1
%25 = load i64, ptr %24, align 8
%eq10 = icmp eq i64 %25, ptrtoint (ptr @"$ct.int" to i64)
br i1 %eq10, label %if.then, label %if.exit
if.then: ; preds = %switch.exit
%16 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%17 = load ptr, ptr %16, align 8
%18 = load i32, ptr %17, align 4
call void (ptr, ...) @printf(ptr @.str.3, i32 %18)
%26 = getelementptr inbounds %any, ptr %z, i32 0, i32 0
%27 = load ptr, ptr %26, align 8
%28 = load i32, ptr %27, align 4
call void (ptr, ...) @printf(ptr @.str.3, i32 %28)
br label %if.exit
if.exit: ; preds = %if.then, %switch.exit

View File

@@ -86,45 +86,105 @@ entry:
switch.entry: ; preds = %entry
%4 = load i64, ptr %switch, align 8
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
br i1 %eq, label %switch.case, label %next_if
br label %check_subtype
switch.case: ; preds = %switch.entry
check_subtype: ; preds = %parent_type_block, %switch.entry
%5 = phi i64 [ %4, %switch.entry ], [ %typeid.parent, %parent_type_block ]
%eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %5
br i1 %eq, label %result_block, label %parent_type_block
parent_type_block: ; preds = %check_subtype
%6 = inttoptr i64 %5 to ptr
%7 = getelementptr inbounds %.introspect, ptr %6, i32 0, i32 1
%typeid.parent = load i64, ptr %7, align 8
%8 = icmp eq i64 %typeid.parent, 0
br i1 %8, label %result_block, label %check_subtype
result_block: ; preds = %parent_type_block, %check_subtype
%9 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
br i1 %9, label %switch.case, label %next_if
switch.case: ; preds = %result_block
call void (ptr, ...) @printf(ptr @.str)
br label %switch.exit
next_if: ; preds = %switch.entry
%eq1 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %4
br i1 %eq1, label %switch.case2, label %next_if3
next_if: ; preds = %result_block
br label %check_subtype1
switch.case2: ; preds = %next_if
check_subtype1: ; preds = %parent_type_block3, %next_if
%10 = phi i64 [ %4, %next_if ], [ %typeid.parent4, %parent_type_block3 ]
%eq2 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %10
br i1 %eq2, label %result_block5, label %parent_type_block3
parent_type_block3: ; preds = %check_subtype1
%11 = inttoptr i64 %10 to ptr
%12 = getelementptr inbounds %.introspect, ptr %11, i32 0, i32 1
%typeid.parent4 = load i64, ptr %12, align 8
%13 = icmp eq i64 %typeid.parent4, 0
br i1 %13, label %result_block5, label %check_subtype1
result_block5: ; preds = %parent_type_block3, %check_subtype1
%14 = phi i1 [ false, %parent_type_block3 ], [ true, %check_subtype1 ]
br i1 %14, label %switch.case6, label %next_if7
switch.case6: ; preds = %result_block5
call void (ptr, ...) @printf(ptr @.str.1)
br label %switch.exit
next_if3: ; preds = %next_if
%eq4 = icmp eq i64 ptrtoint (ptr @"$ct.any" to i64), %4
br i1 %eq4, label %switch.case5, label %next_if6
next_if7: ; preds = %result_block5
br label %check_subtype8
switch.case5: ; preds = %next_if3
check_subtype8: ; preds = %parent_type_block10, %next_if7
%15 = phi i64 [ %4, %next_if7 ], [ %typeid.parent11, %parent_type_block10 ]
%eq9 = icmp eq i64 ptrtoint (ptr @"$ct.any" to i64), %15
br i1 %eq9, label %result_block12, label %parent_type_block10
parent_type_block10: ; preds = %check_subtype8
%16 = inttoptr i64 %15 to ptr
%17 = getelementptr inbounds %.introspect, ptr %16, i32 0, i32 1
%typeid.parent11 = load i64, ptr %17, align 8
%18 = icmp eq i64 %typeid.parent11, 0
br i1 %18, label %result_block12, label %check_subtype8
result_block12: ; preds = %parent_type_block10, %check_subtype8
%19 = phi i1 [ false, %parent_type_block10 ], [ true, %check_subtype8 ]
br i1 %19, label %switch.case13, label %next_if14
switch.case13: ; preds = %result_block12
call void (ptr, ...) @printf(ptr @.str.2)
br label %switch.exit
next_if6: ; preds = %next_if3
%eq7 = icmp eq i64 ptrtoint (ptr @"$ct.p$int" to i64), %4
br i1 %eq7, label %switch.case8, label %next_if9
next_if14: ; preds = %result_block12
br label %check_subtype15
switch.case8: ; preds = %next_if6
check_subtype15: ; preds = %parent_type_block17, %next_if14
%20 = phi i64 [ %4, %next_if14 ], [ %typeid.parent18, %parent_type_block17 ]
%eq16 = icmp eq i64 ptrtoint (ptr @"$ct.p$int" to i64), %20
br i1 %eq16, label %result_block19, label %parent_type_block17
parent_type_block17: ; preds = %check_subtype15
%21 = inttoptr i64 %20 to ptr
%22 = getelementptr inbounds %.introspect, ptr %21, i32 0, i32 1
%typeid.parent18 = load i64, ptr %22, align 8
%23 = icmp eq i64 %typeid.parent18, 0
br i1 %23, label %result_block19, label %check_subtype15
result_block19: ; preds = %parent_type_block17, %check_subtype15
%24 = phi i1 [ false, %parent_type_block17 ], [ true, %check_subtype15 ]
br i1 %24, label %switch.case20, label %next_if21
switch.case20: ; preds = %result_block19
call void (ptr, ...) @printf(ptr @.str.3)
br label %switch.exit
next_if9: ; preds = %next_if6
next_if21: ; preds = %result_block19
br label %switch.default
switch.default: ; preds = %next_if9
switch.default: ; preds = %next_if21
call void (ptr, ...) @printf(ptr @.str.4)
br label %switch.exit
switch.exit: ; preds = %switch.default, %switch.case8, %switch.case5, %switch.case2, %switch.case
switch.exit: ; preds = %switch.default, %switch.case20, %switch.case13, %switch.case6, %switch.case
ret void
}

View File

@@ -0,0 +1,91 @@
module switch_subtype @test;
struct Foo
{
inline Baz g;
int a;
}
struct Bar
{
inline Foo f;
}
struct Baz
{
int z;
}
fn void switch_subtyping()
{
Foo f = {};
f.z = 123;
any x = &f;
switch (x)
{
case int:
assert(false);
case Baz:
assert(true);
default:
assert(false);
}
switch (x)
{
case int:
assert(false);
case Foo:
assert(true);
default:
assert(false);
}
switch (x)
{
case int:
assert(false);
case Bar:
assert(false);
default:
assert(true);
}
switch (Bar.typeid)
{
case int:
assert(false);
case Baz:
assert(true);
case Foo:
assert(false);
case Bar:
assert(false);
default:
assert(false);
}
switch (Bar.typeid)
{
case int:
assert(false);
case Foo:
assert(true);
case Bar:
assert(false);
default:
assert(false);
}
$switch (Bar.typeid)
$case Foo:
assert(true);
$case Bar:
assert(false);
$default:
assert(false);
$endswitch
}
fn void is_subtype()
{
$assert(types::is_subtype_of(Bar, Baz));
$assert(!types::is_subtype_of(Baz, Bar));
typeid baz = Baz.typeid;
typeid bar = Bar.typeid;
assert(bar.is_subtype_of(baz));
assert(!bar.is_subtype_of(double.typeid));
assert(!baz.is_subtype_of(bar));
}