Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler] Document all casts during generics specialization #1187

Merged
merged 2 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/samlang-ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ pub use loc::{Location, Position};
mod reason;
pub use reason::{Description, Reason};

/// HIR is the result of direct lowering from source.
/// Generics are still preserved.
pub mod hir;
mod hir_tests;
/// LIR is the first IR where we start to lose track of accurate types.
/// In this stage, we start to have GC specific instructions.
pub mod lir;
mod lir_tests;
/// MIR is the result of generics specialization.
/// Within generics specialization, some representations of enum types are also optimized.
/// Most of the optimizations run on MIR.
pub mod mir;
mod mir_tests;
/// The full-fidelity representation of the source code.
/// All of the LSP logic runs on source AST.
pub mod source;
mod source_tests;
/// The final stage AST that closely models parts of WASM that are relevant to samlang.
/// For now, this is the only supported backend.
pub mod wasm;
19 changes: 19 additions & 0 deletions crates/samlang-compiler/src/mir_generics_specialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ impl Rewriter {
mir::Expression::int(i32::try_from(*tag * 2 + 1).unwrap()),
));
let mut nested_stmts = vec![];
// Once we pass the check, we can cast the general enum type to a
// more specific subtype.
nested_stmts.push(mir::Statement::Cast {
name: casted_collector,
type_: subtype,
Expand Down Expand Up @@ -147,6 +149,8 @@ impl Rewriter {
),
});
}
// If we are pattern matching on an unboxed case, it means that `unboxed_t` must be a
// pointer type. Therefore, we only need to check whether the test expression is a pointer.
mir::EnumTypeDefinition::Unboxed(unboxed_t) => {
debug_assert!(bindings.len() == 1);
let binded_name = bindings[0].as_ref().unwrap().0;
Expand All @@ -155,37 +159,47 @@ impl Rewriter {
let comparison_temp_2 = heap.alloc_temp_str();
let comparison_temp_3 = heap.alloc_temp_str();
let comparison_temp_4 = heap.alloc_temp_str();
// We need to case the high level expression into int so we can
// do low-level bitwise is-pointer check.
collector.push(mir::Statement::Cast {
name: casted_int_collector,
type_: mir::INT_TYPE,
assigned_expression: test_expr,
});
// Here we test whether this is a pointer
// We got lucky in the JS output, since Number([1,2]) == NaN,
// and the rest happens to work out.
// i < 1024 (small int is not a pointer)
collector.push(mir::Statement::binary(
comparison_temp_1,
hir::Operator::LT,
mir::Expression::var_name(casted_int_collector, mir::INT_TYPE),
mir::Expression::int(1024),
));
// i & 1 (LSB == 1 is not a pointer)
collector.push(mir::Statement::binary(
comparison_temp_2,
hir::Operator::LAND,
mir::Expression::var_name(casted_int_collector, mir::INT_TYPE),
mir::ONE,
));
// (i < 1024) || (i & 1) -> not a pointer
collector.push(mir::Statement::binary(
comparison_temp_3,
hir::Operator::LOR,
mir::Expression::var_name(comparison_temp_1, mir::INT_TYPE),
mir::Expression::var_name(comparison_temp_2, mir::INT_TYPE),
));
// invert the previous check, is a pointer
collector.push(mir::Statement::binary(
comparison_temp_4,
hir::Operator::XOR,
mir::Expression::var_name(comparison_temp_3, mir::INT_TYPE),
mir::ONE,
));
let mut nested_stmts = vec![];
// Once we pass the is-pointer check, we can cast the test expression to the underlying
// unboxed pointer type.
nested_stmts.push(mir::Statement::Cast {
name: binded_name,
type_: *unboxed_t,
Expand All @@ -206,6 +220,8 @@ impl Rewriter {
mir::EnumTypeDefinition::Int => {
let casted_collector = heap.alloc_temp_str();
let comparison_temp = heap.alloc_temp_str();
// We cast the test expression to int to perform the test.
// Once we have i31 type, the test should be performed on i31.
collector.push(mir::Statement::Cast {
name: casted_collector,
type_: mir::INT_TYPE,
Expand Down Expand Up @@ -320,6 +336,7 @@ impl Rewriter {
)
.collect(),
});
// Cast from more specific subtype to the general enum type.
collector.push(mir::Statement::Cast {
name: *enum_variable_name,
type_: mir::Type::Id(enum_type),
Expand All @@ -328,6 +345,7 @@ impl Rewriter {
}
mir::EnumTypeDefinition::Unboxed(_) => {
debug_assert_eq!(associated_data_list.len(), 1);
// Cast from more specific subtype to the general enum type.
collector.push(mir::Statement::Cast {
name: *enum_variable_name,
type_: mir::Type::Id(enum_type),
Expand All @@ -340,6 +358,7 @@ impl Rewriter {
}
mir::EnumTypeDefinition::Int => {
debug_assert!(associated_data_list.is_empty());
// Cast from more specific subtype to the general enum type.
collector.push(mir::Statement::Cast {
name: *enum_variable_name,
type_: mir::Type::Id(enum_type),
Expand Down
Loading