Skip to content

Commit 3a3e0d2

Browse files
authored
refactor: overhaul CLI architecture and enhance backend type handling (#279)
This commit introduces a structured CLI command system, modularizes flag handling, and improves the LLVM backend's ability to handle implicit pointer casting and array-literal addressing. Changes: - **CLI Overhaul**: - Replaced the imperative argument loop in `main.rs` with a structured dispatch system in `src/cli.rs`. - Introduced `Command` and `Global` enums to manage subcommands (`run`, `build`, `img`, `install`, `update`) and global options. - Refactored `src/errors.rs` to use a unified `CliError::Usage` variant, simplifying error reporting. - Moved flag definitions to `src/flags.rs`, adding support for link libraries (`--link`) and search paths (`-L`). - Updated `src/lib.rs` to export a cleaner API. - **LLVM Backend Enhancements**: - **Address-of Operator**: Heavily refactored `gen_addressof` in `pointers.rs` to support taking the address of `ArrayLiteral` expressions. It now generates an alloca, stores elements, and returns the pointer (with bitcasting if necessary). - **Implicit Casting**: Updated variable resolution and address generation to perform automatic `bit_cast` when the actual pointer type differs from the `expected_type`. - **Type Inference**: Passed `expected_type` down to variable generation to allow context-aware LLVM IR emission. - **Linker & Runner Updates**: - Updated `runner.rs` to propagate `LinkFlags` (libraries and paths) to the object linking phase. - Renamed standard library management functions in `src/std.rs` (e.g., `std_install`) for better naming consistency. - **Cleanup**: - Removed unused imports in `control.rs`. - Simplified assembly clobber normalization logic. These changes significantly improve the maintainability of the compiler's entry point and provide a more robust type-coercion system in the backend.
1 parent c68a8f0 commit 3a3e0d2

File tree

13 files changed

+531
-417
lines changed

13 files changed

+531
-417
lines changed

llvm_temporary/src/llvm_temporary/expression/rvalue/dispatch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub(crate) fn gen_expr<'ctx, 'a>(
1010
) -> BasicValueEnum<'ctx> {
1111
match expr {
1212
Expression::Literal(lit) => literals::gen(env, lit, expected_type),
13-
Expression::Variable(name) => variables::gen(env, name),
13+
Expression::Variable(name) => variables::gen(env, name, expected_type),
1414

1515
Expression::Deref(inner) => pointers::gen_deref(env, inner),
1616
Expression::AddressOf(inner) => pointers::gen_addressof(env, inner, expected_type),

llvm_temporary/src/llvm_temporary/expression/rvalue/pointers.rs

Lines changed: 83 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::ExprGenEnv;
2-
use inkwell::types::{BasicType, BasicTypeEnum};
2+
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum};
33
use inkwell::values::{BasicValue, BasicValueEnum};
44
use parser::ast::Expression;
55
use crate::llvm_temporary::llvm_codegen::generate_address_ir;
@@ -73,53 +73,90 @@ pub(crate) fn gen_addressof<'ctx, 'a>(
7373
inner_expr: &Expression,
7474
expected_type: Option<BasicTypeEnum<'ctx>>,
7575
) -> BasicValueEnum<'ctx> {
76-
if let Some(BasicTypeEnum::PointerType(ptr_ty)) = expected_type {
77-
match inner_expr {
78-
Expression::ArrayLiteral(elements) => unsafe {
79-
let array_type = ptr_ty.get_element_type().into_array_type();
80-
let elem_type = array_type.get_element_type();
81-
82-
let array_type = elem_type.array_type(elements.len() as u32);
83-
let tmp_alloca = env.builder.build_alloca(array_type, "tmp_array").unwrap();
84-
85-
for (i, expr) in elements.iter().enumerate() {
86-
let val = env.gen(expr, Some(elem_type));
87-
88-
let gep = env
89-
.builder
90-
.build_in_bounds_gep(
91-
tmp_alloca,
92-
&[
93-
env.context.i32_type().const_zero(),
94-
env.context.i32_type().const_int(i as u64, false),
95-
],
96-
&format!("array_idx_{}", i),
97-
)
98-
.unwrap();
99-
100-
env.builder.build_store(gep, val).unwrap();
101-
}
102-
103-
let alloca = env
104-
.builder
105-
.build_alloca(tmp_alloca.get_type(), "tmp_array_ptr")
106-
.unwrap();
107-
108-
env.builder.build_store(alloca, tmp_alloca).unwrap();
109-
alloca.as_basic_value_enum()
110-
},
111-
112-
Expression::Variable(var_name) => {
113-
let ptr = env
114-
.variables
115-
.get(var_name)
116-
.unwrap_or_else(|| panic!("Variable {} not found", var_name));
117-
ptr.ptr.as_basic_value_enum()
76+
if let Expression::ArrayLiteral(elements) = inner_expr {
77+
let ptr_ty = match expected_type {
78+
Some(BasicTypeEnum::PointerType(p)) => p,
79+
_ => panic!("&[ ... ] needs an expected pointer type (e.g. ptr<i32>)"),
80+
};
81+
82+
let elem_any = ptr_ty.get_element_type();
83+
let elem_basic: BasicTypeEnum<'ctx> = match elem_any {
84+
AnyTypeEnum::IntType(t) => t.into(),
85+
AnyTypeEnum::FloatType(t) => t.into(),
86+
AnyTypeEnum::PointerType(t) => t.into(),
87+
AnyTypeEnum::StructType(t) => t.into(),
88+
AnyTypeEnum::ArrayType(t) => t.into(),
89+
AnyTypeEnum::VectorType(t) => t.into(),
90+
other => panic!("&[ ... ] unsupported element type: {:?}", other),
91+
};
92+
93+
let array_ty = elem_basic.array_type(elements.len() as u32);
94+
let arr_alloca = env.builder.build_alloca(array_ty, "tmp_array").unwrap();
95+
96+
for (i, expr) in elements.iter().enumerate() {
97+
let val = env.gen(expr, Some(elem_basic));
98+
99+
let gep = unsafe {
100+
env.builder
101+
.build_in_bounds_gep(
102+
arr_alloca,
103+
&[
104+
env.context.i32_type().const_zero(),
105+
env.context.i32_type().const_int(i as u64, false),
106+
],
107+
&format!("array_idx_{}", i),
108+
)
109+
.unwrap()
110+
};
111+
112+
env.builder.build_store(gep, val).unwrap();
113+
}
114+
115+
if elem_basic == ptr_ty.get_element_type().try_into().unwrap() {
116+
let first = unsafe {
117+
env.builder
118+
.build_in_bounds_gep(
119+
arr_alloca,
120+
&[
121+
env.context.i32_type().const_zero(),
122+
env.context.i32_type().const_zero(),
123+
],
124+
"array_first_ptr",
125+
)
126+
.unwrap()
127+
};
128+
129+
if first.get_type() != ptr_ty {
130+
return env.builder
131+
.build_bit_cast(first, ptr_ty, "addrof_array_cast")
132+
.unwrap()
133+
.as_basic_value_enum();
118134
}
119135

120-
_ => panic!("& operator must be used on variable name or array literal"),
136+
return first.as_basic_value_enum();
121137
}
122-
} else {
123-
panic!("Expected pointer type for AddressOf");
138+
139+
return arr_alloca.as_basic_value_enum();
124140
}
141+
142+
let addr = generate_address_ir(
143+
env.context,
144+
env.builder,
145+
inner_expr,
146+
env.variables,
147+
env.module,
148+
env.struct_types,
149+
env.struct_field_indices,
150+
);
151+
152+
if let Some(BasicTypeEnum::PointerType(ptr_ty)) = expected_type {
153+
if addr.get_type() != ptr_ty {
154+
return env.builder
155+
.build_bit_cast(addr, ptr_ty, "addrof_cast")
156+
.unwrap()
157+
.as_basic_value_enum();
158+
}
159+
}
160+
161+
addr.as_basic_value_enum()
125162
}

llvm_temporary/src/llvm_temporary/expression/rvalue/variables.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use inkwell::AddressSpace;
22
use super::ExprGenEnv;
3-
use inkwell::types::AnyTypeEnum;
3+
use inkwell::types::{AnyTypeEnum, BasicTypeEnum};
44
use inkwell::values::{BasicValue, BasicValueEnum};
55

6-
pub(crate) fn gen<'ctx, 'a>(env: &mut ExprGenEnv<'ctx, 'a>, var_name: &str) -> BasicValueEnum<'ctx> {
6+
pub(crate) fn gen<'ctx, 'a>(
7+
env: &mut ExprGenEnv<'ctx, 'a>,
8+
var_name: &str,
9+
expected_type: Option<BasicTypeEnum<'ctx>>,
10+
) -> BasicValueEnum<'ctx> {
711
if var_name == "true" {
812
return env.context.bool_type().const_int(1, false).as_basic_value_enum();
913
} else if var_name == "false" {
@@ -21,13 +25,28 @@ pub(crate) fn gen<'ctx, 'a>(env: &mut ExprGenEnv<'ctx, 'a>, var_name: &str) -> B
2125
}
2226

2327
if let Some(var_info) = env.variables.get(var_name) {
24-
let var_type = var_info.ptr.get_type().get_element_type();
28+
let ptr = var_info.ptr;
2529

26-
match var_type {
27-
AnyTypeEnum::ArrayType(_) => var_info.ptr.as_basic_value_enum(),
30+
if let Some(et) = expected_type {
31+
if et.is_pointer_type() {
32+
let expected_ptr = et.into_pointer_type();
33+
if ptr.get_type() != expected_ptr {
34+
return env
35+
.builder
36+
.build_bit_cast(ptr, expected_ptr, &format!("{}_as_ptr", var_name))
37+
.unwrap()
38+
.as_basic_value_enum();
39+
}
40+
return ptr.as_basic_value_enum();
41+
}
42+
}
43+
44+
let elem_ty = ptr.get_type().get_element_type();
45+
match elem_ty {
46+
AnyTypeEnum::ArrayType(_) => ptr.as_basic_value_enum(),
2847
_ => env
2948
.builder
30-
.build_load(var_info.ptr, var_name)
49+
.build_load(ptr, &format!("load_{}", var_name))
3150
.unwrap()
3251
.as_basic_value_enum(),
3352
}
@@ -36,4 +55,4 @@ pub(crate) fn gen<'ctx, 'a>(env: &mut ExprGenEnv<'ctx, 'a>, var_name: &str) -> B
3655
} else {
3756
panic!("variable '{}' not found in current scope", var_name);
3857
}
39-
}
58+
}

llvm_temporary/src/llvm_temporary/llvm_codegen/plan.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ fn normalize_clobber_item(s: &str) -> String {
233233
panic!("Invalid clobber token: '{}'", inner);
234234
}
235235

236-
// "{...}" 형태
237236
if let Some(inner) = t.strip_prefix('{').and_then(|x| x.strip_suffix('}')) {
238237
let n = normalize_token(inner);
239238

llvm_temporary/src/llvm_temporary/statement/control.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::llvm_temporary::llvm_codegen::VariableInfo;
22
use crate::llvm_temporary::expression::rvalue::generate_expression_ir;
33
use inkwell::basic_block::BasicBlock;
44
use inkwell::module::Module;
5-
use inkwell::types::{AnyTypeEnum, BasicTypeEnum, StructType};
6-
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue};
5+
use inkwell::types::{StructType};
6+
use inkwell::values::{BasicValueEnum, FunctionValue};
77
use inkwell::{FloatPredicate, IntPredicate};
88
use parser::ast::{ASTNode, Expression};
99
use std::collections::HashMap;

0 commit comments

Comments
 (0)