diff --git a/cranelift/filetests/verifier/return_at_end.cton b/cranelift/filetests/verifier/return_at_end.cton new file mode 100644 index 0000000000..b0fe05889a --- /dev/null +++ b/cranelift/filetests/verifier/return_at_end.cton @@ -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 +} diff --git a/lib/cretonne/meta/base/settings.py b/lib/cretonne/meta/base/settings.py index 46ff16d851..2baf9927ce 100644 --- a/lib/cretonne/meta/base/settings.py +++ b/lib/cretonne/meta/base/settings.py @@ -28,6 +28,16 @@ enable_verifier = BoolSetting( 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") enable_float = BoolSetting( diff --git a/lib/cretonne/src/ir/layout.rs b/lib/cretonne/src/ir/layout.rs index 6d16b004e1..5c908812cf 100644 --- a/lib/cretonne/src/ir/layout.rs +++ b/lib/cretonne/src/ir/layout.rs @@ -374,6 +374,11 @@ impl Layout { self.first_ebb } + /// Get the last EBB in the layout. + pub fn last_ebb(&self) -> Option { + self.last_ebb + } + /// Get the block following `ebb` in the layout order. pub fn next_ebb(&self, ebb: Ebb) -> Option { self.ebbs[ebb].next.expand() diff --git a/lib/cretonne/src/settings.rs b/lib/cretonne/src/settings.rs index 999d305439..e45dba395c 100644 --- a/lib/cretonne/src/settings.rs +++ b/lib/cretonne/src/settings.rs @@ -319,6 +319,7 @@ mod tests { opt_level = \"default\"\n\ enable_verifier = false\n\ is_64bit = false\n\ + return_at_end = false\n\ is_compressed = false\n\ enable_float = true\n\ enable_simd = true\n\ diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index e6f9ea933e..f3952606ca 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -942,6 +942,21 @@ impl<'a> Verifier<'a> { 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 { self.verify_global_vars()?; 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(()) } }