@@ -15,7 +15,7 @@ use powdr_ast::parsed::{
15
15
use powdr_number:: { BigUint , FieldElement } ;
16
16
use powdr_parser_util:: SourceRef ;
17
17
18
- use crate :: common:: { instruction_flag, RETURN_NAME } ;
18
+ use crate :: common:: { instruction_flag, output_registers , RETURN_NAME } ;
19
19
use crate :: {
20
20
common:: { input_at, output_at, RESET_NAME } ,
21
21
utils:: {
@@ -55,10 +55,55 @@ fn pad_return_arguments(s: &mut FunctionStatement, output_count: usize) {
55
55
} ;
56
56
}
57
57
58
- pub fn generate_machine_rom < T : FieldElement > ( mut machine : Machine ) -> ( Machine , Option < Rom > ) {
58
+ /// Generate the ROM for a machine
59
+ /// Arguments:
60
+ /// - `machine`: the machine to generate the ROM for
61
+ /// - `is_callable`: whether the machine is callable.
62
+ /// - If it is, a dispatcher is generated and this machine can be used as a submachine
63
+ /// - If it is not, the machine must have a single function which never returns, so that the entire trace can be filled with a single block
64
+ pub fn generate_machine_rom < T : FieldElement > (
65
+ mut machine : Machine ,
66
+ is_callable : bool ,
67
+ ) -> ( Machine , Option < Rom > ) {
59
68
if !machine. has_pc ( ) {
60
69
// do nothing, there is no rom to be generated
61
70
( machine, None )
71
+ } else if !is_callable {
72
+ let pc = machine. pc ( ) . unwrap ( ) ;
73
+ // if the machine is not callable, it must have a single function
74
+ assert_eq ! ( machine. callable. 0 . len( ) , 1 ) ;
75
+ let callable = machine. callable . iter_mut ( ) . next ( ) . unwrap ( ) ;
76
+ let function = match callable. symbol {
77
+ CallableSymbol :: Function ( ref mut f) => f,
78
+ CallableSymbol :: Operation ( _) => unreachable ! ( ) ,
79
+ } ;
80
+ // the function must have no inputs
81
+ assert ! ( function. params. inputs. is_empty( ) ) ;
82
+ // the function must have no outputs
83
+ assert ! ( function. params. outputs. is_empty( ) ) ;
84
+ // we implement `return` as an infinite loop
85
+ machine. instructions . push ( {
86
+ // `return` is a protected keyword, so we use a dummy name and replace it afterwards
87
+ let mut d = parse_instruction_definition ( & format ! ( "instr dummy {{ {pc}' = {pc} }}" , ) ) ;
88
+ d. name = RETURN_NAME . into ( ) ;
89
+ d
90
+ } ) ;
91
+
92
+ let rom = std:: mem:: take ( & mut function. body . statements )
93
+ . into_iter_batches ( )
94
+ . collect ( ) ;
95
+
96
+ * callable. symbol = OperationSymbol {
97
+ source : SourceRef :: unknown ( ) ,
98
+ id : OperationId { id : None } ,
99
+ params : Params {
100
+ inputs : vec ! [ ] ,
101
+ outputs : vec ! [ ] ,
102
+ } ,
103
+ }
104
+ . into ( ) ;
105
+
106
+ ( machine, Some ( Rom { statements : rom } ) )
62
107
} else {
63
108
// all callables in the machine must be functions
64
109
assert ! ( machine. callable. is_only_functions( ) ) ;
@@ -67,24 +112,6 @@ pub fn generate_machine_rom<T: FieldElement>(mut machine: Machine) -> (Machine,
67
112
68
113
let pc = machine. pc ( ) . unwrap ( ) ;
69
114
70
- // add the necessary embedded instructions
71
- let embedded_instructions = [
72
- parse_instruction_definition ( & format ! (
73
- "instr _jump_to_operation {{ {pc}' = {operation_id} }}" ,
74
- ) ) ,
75
- parse_instruction_definition ( & format ! (
76
- "instr {RESET_NAME} {{ {} }}" ,
77
- machine
78
- . write_register_names( )
79
- . map( |w| format!( "{w}' = 0" ) )
80
- . collect:: <Vec <_>>( )
81
- . join( ", " )
82
- ) ) ,
83
- parse_instruction_definition ( & format ! ( "instr _loop {{ {pc}' = {pc} }}" ) ) ,
84
- ] ;
85
-
86
- machine. instructions . extend ( embedded_instructions) ;
87
-
88
115
// generate the rom
89
116
// the functions are already batched, we just batch the dispatcher manually here
90
117
@@ -124,6 +151,33 @@ pub fn generate_machine_rom<T: FieldElement>(mut machine: Machine) -> (Machine,
124
151
input_assignment_registers_declarations. chain ( output_assignment_registers_declarations) ,
125
152
) ;
126
153
154
+ // add the necessary embedded instructions
155
+ let embedded_instructions = [
156
+ parse_instruction_definition ( & format ! (
157
+ "instr _jump_to_operation {{ {pc}' = {operation_id} }}" ,
158
+ ) ) ,
159
+ parse_instruction_definition ( & format ! (
160
+ "instr {RESET_NAME} {{ {} }}" ,
161
+ machine
162
+ . write_register_names( )
163
+ . map( |w| format!( "{w}' = 0" ) )
164
+ . collect:: <Vec <_>>( )
165
+ . join( ", " )
166
+ ) ) ,
167
+ parse_instruction_definition ( & format ! ( "instr _loop {{ {pc}' = {pc} }}" ) ) ,
168
+ {
169
+ // `return` is a protected keyword, so we use a dummy name and replace it afterwards
170
+ let mut d = parse_instruction_definition ( & format ! (
171
+ "instr dummy {} {{ {pc}' = 0 }}" ,
172
+ output_registers( output_count) . join( ", " )
173
+ ) ) ;
174
+ d. name = RETURN_NAME . into ( ) ;
175
+ d
176
+ } ,
177
+ ] ;
178
+
179
+ machine. instructions . extend ( embedded_instructions) ;
180
+
127
181
// turn each function into an operation, setting the operation_id to the current position in the ROM
128
182
for callable in machine. callable . iter_mut ( ) {
129
183
let operation_id = BigUint :: from ( rom. len ( ) as u64 ) ;
@@ -264,7 +318,7 @@ mod tests {
264
318
let checked = powdr_analysis:: machine_check:: check ( parsed) . unwrap ( ) ;
265
319
checked
266
320
. into_machines ( )
267
- . map ( |( name, m) | ( name, generate_machine_rom :: < T > ( m) ) )
321
+ . map ( |( name, m) | ( name, generate_machine_rom :: < T > ( m, true ) ) )
268
322
. collect ( )
269
323
}
270
324
0 commit comments