Skip to content

Commit

Permalink
Generalize global array initialization. Add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
maximecb committed Oct 9, 2023
1 parent a9761b2 commit abe293c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 83 deletions.
138 changes: 55 additions & 83 deletions ncc/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,58 @@ impl SymGen
}
}

fn gen_global_init(t: &Type, init_expr: &Option<Expr>, out: &mut String) -> Result<(), ParseError>
{
match (t, init_expr) {
(_, None) => {
out.push_str(&format!(".zero {};\n", t.sizeof()));
}

(Type::UInt(n), Some(Expr::Int(v))) => {
out.push_str(&format!(".u{} {};\n", n, v))
}

(Type::Int(n), Some(Expr::Int(v))) => {
out.push_str(&format!(".i{} {};\n", n, v))
}

(Type::Float(32), Some(Expr::Float32(v))) => {
out.push_str(&format!(".f32 {};\n", v))
}

(Type::Pointer(_), Some(Expr::Int(v))) => {
out.push_str(&format!(".u64 {};\n", v))
}

// Pointer to a global array
(Type::Pointer(_), Some(Expr::Ref(Decl::Global { name, t: Array { .. } } ))) => {
out.push_str(&format!(".addr64 {};\n", name))
}

// Global string constant
(Type::Array { elem_type, size_expr }, Some(Expr::String(s))) => {
match (elem_type.as_ref(), size_expr.as_ref()) {
(Type::UInt(8), Expr::Int(n)) => {
assert!(*n as usize == s.bytes().len() + 1);
out.push_str(&format!(".stringz \"{}\";\n", s.escape_default()))
}
_ => panic!()
}
}

// Global array with initializer expression
(Type::Array {..}, Some(init_expr)) => {
gen_array_init(t, init_expr, out)?;
}

_ => todo!("{:?} {:?}", t, init_expr)
}

Ok(())
}

// FIXME: ideally, all error checking should be done before we get to the
// codegen, so that codegen can't return an error?

fn gen_array_init(array_type: &Type, init_expr: &Expr, out: &mut String) -> Result<(), ParseError>
{
// Get the type of the initializer expression
Expand All @@ -43,45 +92,9 @@ fn gen_array_init(array_type: &Type, init_expr: &Expr, out: &mut String) -> Resu
_ => return ParseError::msg_only("invalid initializer for global array variable")
};

match array_elem_t {
// Array of signed integers
Type::Int(n) => {
for expr in elem_exprs {
match expr {
Expr::Int(v) => out.push_str(&format!(".i{} {};\n", n, v)),
_ => panic!()
}
}
}

// Array of unsigned integers
Type::UInt(n) => {
for expr in elem_exprs {
match expr {
Expr::Int(v) => out.push_str(&format!(".u{} {};\n", n, v)),
_ => panic!()
}
}
}

// Array of floats
Type::Float(32) => {
for expr in elem_exprs {
match expr {
Expr::Float32(v) => out.push_str(&format!(".f32 {};\n", v)),
_ => panic!()
}
}
}

// Array of arrays (n-dimensional array)
Type::Array {..} => {
for expr in elem_exprs {
gen_array_init(&array_elem_t, expr, out)?;
}
}

_ => panic!("unable to initialize global array")
// Generate initialization data for each element expression
for expr in elem_exprs {
gen_global_init(&array_elem_t, &Some(expr.clone()), out)?;
}

Ok(())
Expand Down Expand Up @@ -119,49 +132,8 @@ impl Unit
// Write a label
out.push_str(&format!("{}:\n", global.name));

match (&global.var_type, &global.init_expr) {
(_, None) => {
out.push_str(&format!(".zero {};\n", global.var_type.sizeof()));
}

(Type::UInt(n), Some(Expr::Int(v))) => {
out.push_str(&format!(".u{} {};\n", n, v))
}

(Type::Int(n), Some(Expr::Int(v))) => {
out.push_str(&format!(".i{} {};\n", n, v))
}

(Type::Float(32), Some(Expr::Float32(v))) => {
out.push_str(&format!(".f32 {};\n", v))
}

(Type::Pointer(_), Some(Expr::Int(v))) => {
out.push_str(&format!(".u64 {};\n", v))
}

// Pointer to a global array
(Type::Pointer(_), Some(Expr::Ref(Decl::Global { name, t: Array { .. } } ))) => {
out.push_str(&format!(".addr64 {};\n", name))
}

// Global string constant
(Type::Array { elem_type, size_expr }, Some(Expr::String(s))) => {
match (elem_type.as_ref(), size_expr.as_ref()) {
(Type::UInt(8), Expr::Int(n)) => {
assert!(*n as usize == s.bytes().len() + 1);
out.push_str(&format!(".stringz \"{}\";\n", s.escape_default()))
}
_ => panic!()
}
}

(Type::Array {..}, Some(init_expr)) => {
gen_array_init(&global.var_type, &init_expr, &mut out)?;
}

_ => todo!("{:?} {:?}", global.var_type, global.init_expr)
}
// Generate initialization data for the global
gen_global_init(&global.var_type, &global.init_expr, &mut out)?;

out.push_str("\n");
}
Expand Down
4 changes: 4 additions & 0 deletions ncc/tests/arrays.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ uint8_t bytes2d[2][2] = { {0, 1}, {2, 3} };

uint8_t array2d[600][800];

char* str_array[3] = { "foo", "bar", "bif" };

// Integer literals of type long and int in the same array literal
uint64_t arr_int_long[2] = { 0x7fff8beb, 0x8000e82a };

Expand All @@ -21,6 +23,8 @@ int main()
assert(bytes2d[1][0] == 2);
assert(bytes2d[1][1] == 3);

assert(str_array[2][1] == 'i');

// Regression: signed integer index needs to be sign-extended
int* p = int_array + 2;
int idx = 1 - 3;
Expand Down
4 changes: 4 additions & 0 deletions ncc/tests/strings.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ uint8_t arr2[19];

char dst[32];

char* global_str = "global string pointer";

int main()
{
assert(strlen("") == 0);
assert(strlen("foo") == 3);
assert(strlen("foo" "bar") == 6);
assert(strlen("()") == 2);
assert(strlen(")") == 1);
assert(global_str);
assert(strlen(global_str) == 21);

assert(strcmp("", "") == 0);
assert(strcmp("bar", "bar") == 0);
Expand Down

0 comments on commit abe293c

Please sign in to comment.