Add a return_at_end setting.

The flag guarantees that the generated function does not have any
internal return instructions. If the function returns at all, the return
must be the last instruction.

For now just implement a verifier check for this property. When we get
CFG simplifiers and block layout optimizations, they will need to heed
the flag.
This commit is contained in:
Jakob Stoklund Olesen
2017-09-11 11:04:38 -07:00
parent 7bf2747e1e
commit 25af6d380b
5 changed files with 61 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
test verifier
set return_at_end
; The verifier doesn't have an API for verifying with flags without also
; setting an ISA.
isa riscv
function %ok(i32) {
ebb0(v0: i32):
brnz v0, ebb1
trap
ebb1:
trapz v0
return
}
function %bad(i32) {
ebb0(v0: i32):
brnz v0, ebb1
return ; error: Internal return not allowed
ebb1:
trapz v0
return
}

View File

@@ -28,6 +28,16 @@ enable_verifier = BoolSetting(
is_64bit = BoolSetting("Enable 64-bit code generation") is_64bit = BoolSetting("Enable 64-bit code generation")
return_at_end = BoolSetting(
"""
Generate functions with at most a single return instruction at the
end of the function.
This guarantees that functions do not have any internal return
instructions. Either they never return, or they have a single return
instruction at the end.
""")
is_compressed = BoolSetting("Enable compressed instructions") is_compressed = BoolSetting("Enable compressed instructions")
enable_float = BoolSetting( enable_float = BoolSetting(

View File

@@ -374,6 +374,11 @@ impl Layout {
self.first_ebb self.first_ebb
} }
/// Get the last EBB in the layout.
pub fn last_ebb(&self) -> Option<Ebb> {
self.last_ebb
}
/// Get the block following `ebb` in the layout order. /// Get the block following `ebb` in the layout order.
pub fn next_ebb(&self, ebb: Ebb) -> Option<Ebb> { pub fn next_ebb(&self, ebb: Ebb) -> Option<Ebb> {
self.ebbs[ebb].next.expand() self.ebbs[ebb].next.expand()

View File

@@ -319,6 +319,7 @@ mod tests {
opt_level = \"default\"\n\ opt_level = \"default\"\n\
enable_verifier = false\n\ enable_verifier = false\n\
is_64bit = false\n\ is_64bit = false\n\
return_at_end = false\n\
is_compressed = false\n\ is_compressed = false\n\
enable_float = true\n\ enable_float = true\n\
enable_simd = true\n\ enable_simd = true\n\

View File

@@ -942,6 +942,21 @@ impl<'a> Verifier<'a> {
Ok(()) Ok(())
} }
/// Verify the `return_at_end` property which requires that there are no internal return
/// instructions.
fn verify_return_at_end(&self) -> Result {
for ebb in self.func.layout.ebbs() {
let inst = self.func.layout.last_inst(ebb).unwrap();
if self.func.dfg[inst].opcode().is_return() &&
Some(ebb) != self.func.layout.last_ebb()
{
return err!(inst, "Internal return not allowed with return_at_end=1");
}
}
Ok(())
}
pub fn run(&self) -> Result { pub fn run(&self) -> Result {
self.verify_global_vars()?; self.verify_global_vars()?;
self.typecheck_entry_block_arguments()?; self.typecheck_entry_block_arguments()?;
@@ -954,6 +969,10 @@ impl<'a> Verifier<'a> {
} }
} }
if self.isa.map(|isa| isa.flags().return_at_end()) == Some(true) {
self.verify_return_at_end()?;
}
Ok(()) Ok(())
} }
} }