diff --git a/src/compiler.bbb b/src/compiler.bbb index e7baa36..888a625 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -6238,7 +6238,30 @@ parse = fn (module: &Module) void if (!is_global_keyword) { - #trap(); + >initial_value = parse_value(module, scope, zero); + skip_space(module); + expect_character(module, ';'); + + >global_storage = new_value(module); + global_storage.& = { + .id = .global, + zero, + }; + + >global = new_global(module); + global.& = { + .variable = { + .storage = global_storage, + .type = global_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .initial_value = initial_value, + .linkage = .internal, // TODO: linkage + zero, + }; } } } @@ -10829,14 +10852,14 @@ emit = fn (module: &Module) void continue; } - >global_pointer_type = global.variable.storage.type; - assert(global_pointer_type.id == .pointer); - >global_value_type = global_pointer_type.content.pointer.element_type; - switch (global.variable.storage.id) { .function, .forward_declared_function => { + >global_pointer_type = global.variable.storage.type; + assert(global_pointer_type.id == .pointer); + >global_value_type = global_pointer_type.content.pointer.element_type; + >function_type = &global_value_type.content.function; >semantic_argument_types = function_type.base.semantic_argument_types; >semantic_return_type = function_type.base.semantic_return_type; @@ -11063,394 +11086,409 @@ emit = fn (module: &Module) void assert(!module.current_macro_instantiation); assert(!module.current_macro_declaration); - if (global.variable.storage.id == .function) + if (global.emitted) { - module.current_function = global; + continue; + } - >function = global.variable.storage; - assert(function.id == .function); - >pointer_type = function.type; - assert(pointer_type.id == .pointer); - >value_type = pointer_type.content.pointer.element_type; - assert(value_type == global.variable.type); - assert(value_type.id == .function); - >function_type = &value_type.content.function; - >semantic_argument_types = function_type.base.semantic_argument_types; - >llvm_function = function.llvm; - assert(llvm_function != zero); - - >llvm_abi_argument_buffer: [64]&LLVMValue = undefined; - >llvm_abi_arguments = llvm_abi_argument_buffer[..function_type.abi.abi_argument_types.length]; - LLVMGetParams(llvm_function, &llvm_abi_argument_buffer[0]); - - >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); - >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); - function.content.function.llvm.return_block = return_block; - - LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); - LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); - - >return_abi = &function_type.abi.return_abi; - switch (return_abi.flags.kind) + switch (global.variable.storage.id) + { + .function, + .forward_declared_function, + => { - .ignore => {}, - .indirect => - { - #trap(); - }, - .in_alloca => - { - #trap(); - }, - else => - { - >alloca = create_alloca(module, { - .type = return_abi.semantic_type, - .name = "return_value", - zero, - }); - function.content.function.llvm.return_alloca = alloca; - }, - } - - >arguments = function.content.function.arguments; - >argument_abis = function_type.abi.argument_abis; - assert(arguments.length == argument_abis.length); + module.current_function = global; - for (i: 0..semantic_argument_types.length) - { - >argument = &arguments[i]; - >argument_abi = &argument_abis[i]; - // TODO: double slice - >argument_abi_arguments = llvm_abi_arguments[#extend(argument_abi.abi_start)..]; - argument_abi_arguments = argument_abi_arguments[..#extend(argument_abi.abi_count)]; + >function = global.variable.storage; + assert(function.id == .function); + >pointer_type = function.type; + assert(pointer_type.id == .pointer); + >value_type = pointer_type.content.pointer.element_type; + assert(value_type == global.variable.type); + assert(value_type.id == .function); + >function_type = &value_type.content.function; + >semantic_argument_types = function_type.base.semantic_argument_types; + >llvm_function = function.llvm; + assert(llvm_function != zero); - >semantic_argument_storage: &LLVMValue = zero; + >llvm_abi_argument_buffer: [64]&LLVMValue = undefined; + >llvm_abi_arguments = llvm_abi_argument_buffer[..function_type.abi.abi_argument_types.length]; + LLVMGetParams(llvm_function, &llvm_abi_argument_buffer[0]); - switch (argument_abi.flags.kind) + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); + >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); + function.content.function.llvm.return_block = return_block; + + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + + >return_abi = &function_type.abi.return_abi; + switch (return_abi.flags.kind) { - .direct, .extend => - { - >first_argument = argument_abi_arguments[0]; - >coerce_to_type = abi_get_coerce_to_type(argument_abi); - - if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type)) + .ignore => {}, + .indirect => { - assert(argument_abi.abi_count == 1); - >is_promoted: u1 = 0; - >v = first_argument; - - if (coerce_to_type.llvm.abi != LLVMTypeOf(v)) - { - #trap(); - } - - if (is_promoted) - { - #trap(); - } - - // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc - if (is_arbitrary_bit_integer(argument_abi.semantic_type)) - { - >bit_count = get_bit_size(argument_abi.semantic_type); - >abi_bit_count = align_bit_count(bit_count); - >is_signed = type_is_signed(argument_abi.semantic_type); - >destination_type = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); - >alloca = create_alloca(module, { - .type = destination_type, - .name = argument.variable.name, - zero, - }); - - >result: &LLVMValue = undefined; - - if (bit_count < abi_bit_count) - { - >llvm_type = destination_type.llvm.memory; - if (is_signed) - { - result = LLVMBuildSExt(module.llvm.builder, first_argument, llvm_type, ""); - } - else - { - result = LLVMBuildZExt(module.llvm.builder, first_argument, llvm_type, ""); - } - } - else - { - #trap(); - } - - create_store(module, { - .source = result, - .destination = alloca, - .type = destination_type, - zero, - }); - - semantic_argument_storage = alloca; - } - else - { - >alloca = create_alloca(module, { - .type = argument_abi.semantic_type, - .name = argument.variable.name, - zero, - }); - - create_store(module, { - .source = first_argument, - .destination = alloca, - .type = argument_abi.semantic_type, - zero, - }); - - semantic_argument_storage = alloca; - } - } - else +#trap(); + }, + .in_alloca => + { +#trap(); + }, + else => { - >is_fixed_vector_type: u1 = 0; - if (is_fixed_vector_type) - { - #trap(); - } - - if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) - { - >contains_homogeneous_scalable_vector_types: u1 = 0; - if (contains_homogeneous_scalable_vector_types) - { - #trap(); - } - } - >alloca = create_alloca(module, { - .type = argument_abi.semantic_type, - .name = argument.variable.name, - zero, - }); + .type = return_abi.semantic_type, + .name = "return_value", + zero, + }); + function.content.function.llvm.return_alloca = alloca; + }, + } - >pointer: &LLVMValue = undefined; - >pointer_type: &Type = undefined; + >arguments = function.content.function.arguments; + >argument_abis = function_type.abi.argument_abis; + assert(arguments.length == argument_abis.length); - if (argument_abi.attributes.direct.offset > 0) - { - #trap(); - } - else - { - pointer = alloca; - pointer_type = argument_abi.semantic_type; - } + for (i: 0..semantic_argument_types.length) + { + >argument = &arguments[i]; + >argument_abi = &argument_abis[i]; + // TODO: double slice + >argument_abi_arguments = llvm_abi_arguments[#extend(argument_abi.abi_start)..]; + argument_abi_arguments = argument_abi_arguments[..#extend(argument_abi.abi_count)]; - if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) - { - >struct_size = get_byte_size(coerce_to_type); - >pointer_element_size = get_byte_size(pointer_type); - >is_scalable: u1 = 0; + >semantic_argument_storage: &LLVMValue = zero; - if (is_scalable) - { - #trap(); - } - else - { - >source_size = struct_size; - >destination_size = pointer_element_size; - >address_alignment = get_byte_alignment(argument_abi.semantic_type); + switch (argument_abi.flags.kind) + { + .direct, .extend => + { + >first_argument = argument_abi_arguments[0]; + >coerce_to_type = abi_get_coerce_to_type(argument_abi); - >address: &LLVMValue = undefined; - - if (source_size <= destination_size) - { - address = alloca; - } - else - { - address = create_alloca(module, { - .type = coerce_to_type, - .name = "coerce", - .alignment = address_alignment, - }); - } - - >fields = coerce_to_type.content.struct.fields; - assert(fields.length == #extend(argument_abi.abi_count)); - - resolve_type_in_place(module, coerce_to_type); - - for (i: 0..fields.length) - { - >field = &fields[i]; - >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, address, #truncate(i), ""); - create_store(module, { - .source = argument_abi_arguments[i], - .destination = gep, - .type = fields[i].type, - zero, - }); - } - - if (source_size > destination_size) - { - >u64_type = uint64(module); - resolve_type_in_place(module, u64_type); - - >memcpy_size = LLVMConstInt(u64_type.llvm.abi, destination_size, 0); - LLVMBuildMemCpy(module.llvm.builder, pointer, address_alignment, address, address_alignment, memcpy_size); - } - } - } - else + if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type)) { assert(argument_abi.abi_count == 1); - >abi_argument_type = function_type.abi.abi_argument_types[argument_abi.abi_start]; - >destination_size: u64 = get_byte_size(pointer_type) - #extend(argument_abi.attributes.direct.offset); - >is_volatile: u1 = 0; - create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + >is_promoted: u1 = 0; + >v = first_argument; + + if (coerce_to_type.llvm.abi != LLVMTypeOf(v)) + { +#trap(); + } + + if (is_promoted) + { +#trap(); + } + + // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc + if (is_arbitrary_bit_integer(argument_abi.semantic_type)) + { + >bit_count = get_bit_size(argument_abi.semantic_type); + >abi_bit_count = align_bit_count(bit_count); + >is_signed = type_is_signed(argument_abi.semantic_type); + >destination_type = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); + >alloca = create_alloca(module, { + .type = destination_type, + .name = argument.variable.name, + zero, + }); + + >result: &LLVMValue = undefined; + + if (bit_count < abi_bit_count) + { + >llvm_type = destination_type.llvm.memory; + if (is_signed) + { + result = LLVMBuildSExt(module.llvm.builder, first_argument, llvm_type, ""); + } + else + { + result = LLVMBuildZExt(module.llvm.builder, first_argument, llvm_type, ""); + } + } + else + { +#trap(); + } + + create_store(module, { + .source = result, + .destination = alloca, + .type = destination_type, + zero, + }); + + semantic_argument_storage = alloca; + } + else + { + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument.variable.name, + zero, + }); + + create_store(module, { + .source = first_argument, + .destination = alloca, + .type = argument_abi.semantic_type, + zero, + }); + + semantic_argument_storage = alloca; + } } + else + { + >is_fixed_vector_type: u1 = 0; + if (is_fixed_vector_type) + { +#trap(); + } - semantic_argument_storage = alloca; - } - }, - .indirect => + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) + { + >contains_homogeneous_scalable_vector_types: u1 = 0; + if (contains_homogeneous_scalable_vector_types) + { +#trap(); + } + } + + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument.variable.name, + zero, + }); + + >pointer: &LLVMValue = undefined; + >pointer_type: &Type = undefined; + + if (argument_abi.attributes.direct.offset > 0) + { +#trap(); + } + else + { + pointer = alloca; + pointer_type = argument_abi.semantic_type; + } + + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) + { + >struct_size = get_byte_size(coerce_to_type); + >pointer_element_size = get_byte_size(pointer_type); + >is_scalable: u1 = 0; + + if (is_scalable) + { +#trap(); + } + else + { + >source_size = struct_size; + >destination_size = pointer_element_size; + >address_alignment = get_byte_alignment(argument_abi.semantic_type); + + >address: &LLVMValue = undefined; + + if (source_size <= destination_size) + { + address = alloca; + } + else + { + address = create_alloca(module, { + .type = coerce_to_type, + .name = "coerce", + .alignment = address_alignment, + }); + } + + >fields = coerce_to_type.content.struct.fields; + assert(fields.length == #extend(argument_abi.abi_count)); + + resolve_type_in_place(module, coerce_to_type); + + for (i: 0..fields.length) + { + >field = &fields[i]; + >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, address, #truncate(i), ""); + create_store(module, { + .source = argument_abi_arguments[i], + .destination = gep, + .type = fields[i].type, + zero, + }); + } + + if (source_size > destination_size) + { + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >memcpy_size = LLVMConstInt(u64_type.llvm.abi, destination_size, 0); + LLVMBuildMemCpy(module.llvm.builder, pointer, address_alignment, address, address_alignment, memcpy_size); + } + } + } + else + { + assert(argument_abi.abi_count == 1); + >abi_argument_type = function_type.abi.abi_argument_types[argument_abi.abi_start]; + >destination_size: u64 = get_byte_size(pointer_type) - #extend(argument_abi.attributes.direct.offset); + >is_volatile: u1 = 0; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } + + semantic_argument_storage = alloca; + } + }, + .indirect => + { +#trap(); + }, + else => + { + unreachable; + }, + } + + assert(semantic_argument_storage != zero); + + >storage = new_value(module); + >value_type = argument.variable.type; + storage.& = { + .type = get_pointer_type(module, value_type), + .id = .argument, + .llvm = semantic_argument_storage, + zero, + }; + argument.variable.storage = storage; + + if (module.has_debug_info) { - #trap(); - }, - else => - { - unreachable; - }, - } - - assert(semantic_argument_storage != zero); - - >storage = new_value(module); - >value_type = argument.variable.type; - storage.& = { - .type = get_pointer_type(module, value_type), - .id = .argument, - .llvm = semantic_argument_storage, - zero, - }; - argument.variable.storage = storage; - - if (module.has_debug_info) - { - emit_debug_argument(module, argument, entry_block); - } - } - - analyze_block(module, function.content.function.block); - - >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); - - if (current_basic_block) - { - assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - if (!LLVMGetFirstInstruction(current_basic_block) or !LLVMGetFirstUse(#pointer_cast(current_basic_block))) - { - LLVMReplaceAllUsesWith(#pointer_cast(return_block), #pointer_cast(current_basic_block)); - LLVMDeleteBasicBlock(return_block); - } - else - { - #trap(); - } - } - else - { - >has_single_jump_to_return_block: u1 = 0; - - >first_use = LLVMGetFirstUse(#pointer_cast(return_block)); - >user: &LLVMValue = zero; - - if (first_use) - { - >second_use = LLVMGetNextUse(first_use); - >has_one_use = first_use != zero and first_use == zero; - - if (has_one_use) - { - user = LLVMGetUser(first_use); - has_single_jump_to_return_block = LLVMIsABranchInst(user) != zero and? LLVMIsConditional(user) != 0 and? LLVMGetSuccessor(user, 0) == return_block; + emit_debug_argument(module, argument, entry_block); } } - if (has_single_jump_to_return_block) + analyze_block(module, function.content.function.block); + + >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); + + if (current_basic_block) { - #trap(); + assert(!LLVMGetBasicBlockTerminator(current_basic_block)); + if (!LLVMGetFirstInstruction(current_basic_block) or !LLVMGetFirstUse(#pointer_cast(current_basic_block))) + { + LLVMReplaceAllUsesWith(#pointer_cast(return_block), #pointer_cast(current_basic_block)); + LLVMDeleteBasicBlock(return_block); + } + else + { +#trap(); + } } else { - emit_block(module, return_block); - } - } + >has_single_jump_to_return_block: u1 = 0; - if (module.has_debug_info) - { - LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); - >subprogram = LLVMGetSubprogram(llvm_function); - LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); - } + >first_use = LLVMGetFirstUse(#pointer_cast(return_block)); + >user: &LLVMValue = zero; - >semantic_return_type = return_abi.semantic_type; - if (semantic_return_type == noreturn_type(module) or function.content.function.attributes.naked) - { - #trap(); - } - else if (semantic_return_type == void_type(module)) - { - LLVMBuildRetVoid(module.llvm.builder); - } - else - { - >return_value: &LLVMValue = zero; - - switch (return_abi.flags.kind) - { - .direct, .extend => + if (first_use) { - >return_alloca = function.content.function.llvm.return_alloca; - >coerce_to_type = abi_get_coerce_to_type(return_abi); - if (type_is_abi_equal(module, coerce_to_type, semantic_return_type) and? return_abi.attributes.direct.offset == 0) + >second_use = LLVMGetNextUse(first_use); + >has_one_use = first_use != zero and first_use == zero; + + if (has_one_use) { - >store = llvm_find_return_value_dominating_store(module.llvm.builder, return_alloca, semantic_return_type.llvm.abi); - if (store) + user = LLVMGetUser(first_use); + has_single_jump_to_return_block = LLVMIsABranchInst(user) != zero and? LLVMIsConditional(user) != 0 and? LLVMGetSuccessor(user, 0) == return_block; + } + } + + if (has_single_jump_to_return_block) + { +#trap(); + } + else + { + emit_block(module, return_block); + } + } + + if (module.has_debug_info) + { + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >subprogram = LLVMGetSubprogram(llvm_function); + LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + } + + >semantic_return_type = return_abi.semantic_type; + if (semantic_return_type == noreturn_type(module) or function.content.function.attributes.naked) + { +#trap(); + } + else if (semantic_return_type == void_type(module)) + { + LLVMBuildRetVoid(module.llvm.builder); + } + else + { + >return_value: &LLVMValue = zero; + + switch (return_abi.flags.kind) + { + .direct, .extend => + { + >return_alloca = function.content.function.llvm.return_alloca; + >coerce_to_type = abi_get_coerce_to_type(return_abi); + if (type_is_abi_equal(module, coerce_to_type, semantic_return_type) and? return_abi.attributes.direct.offset == 0) { - return_value = LLVMGetOperand(store, 0); - >alloca = LLVMGetOperand(store, 1); - assert(alloca == return_alloca); - LLVMInstructionEraseFromParent(store); - LLVMInstructionEraseFromParent(alloca); + >store = llvm_find_return_value_dominating_store(module.llvm.builder, return_alloca, semantic_return_type.llvm.abi); + if (store) + { + return_value = LLVMGetOperand(store, 0); + >alloca = LLVMGetOperand(store, 1); + assert(alloca == return_alloca); + LLVMInstructionEraseFromParent(store); + LLVMInstructionEraseFromParent(alloca); + } + else + { + return_value = create_load(module, { + .type = semantic_return_type, + .pointer = return_alloca, + zero, + }); + } } else { - return_value = create_load(module, { - .type = semantic_return_type, - .pointer = return_alloca, - zero, - }); +#trap(); } - } - else - { - #trap(); - } - }, - .indirect => - { - #trap(); - }, + }, + .indirect => + { +#trap(); + }, + } + + LLVMBuildRet(module.llvm.builder, return_value); } - LLVMBuildRet(module.llvm.builder, return_value); - } - - // END OF SCOPE - module.current_function = zero; + // END OF SCOPE + module.current_function = zero; + }, + .global => + { + #trap(); + }, + else => { report_error(); }, } global = global.next; @@ -11783,6 +11821,7 @@ names: [_][]u8 = [ "pointer_cast", "u1_return", "local_type_inference", + "global", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32