7474use fidget_core:: { compiler:: RegOp , vm:: VmData } ;
7575use zerocopy:: IntoBytes ;
7676
77- pub use fidget_core:: compiler:: RegOpDiscriminants as BytecodeOp ;
77+ /// Error type for bytecode builder
78+ #[ derive( thiserror:: Error , Debug , PartialEq ) ]
79+ #[ error( "register 255 is reserved" ) ]
80+ pub struct ReservedRegister ;
81+
82+ #[ derive(
83+ Copy ,
84+ Clone ,
85+ Debug ,
86+ PartialEq ,
87+ serde:: Serialize ,
88+ serde:: Deserialize ,
89+ strum:: EnumIter ,
90+ strum:: EnumCount ,
91+ strum:: IntoStaticStr ,
92+ strum:: FromRepr ,
93+ ) ]
94+ enum BytecodeOp {
95+ Copy ,
96+ Neg ,
97+ Abs ,
98+ Recip ,
99+ Sqrt ,
100+ Square ,
101+ Floor ,
102+ Ceil ,
103+ Round ,
104+ Sin ,
105+ Cos ,
106+ Tan ,
107+ Asin ,
108+ Acos ,
109+ Atan ,
110+ Exp ,
111+ Ln ,
112+ Not ,
113+ Add ,
114+ Sub ,
115+ Mul ,
116+ Div ,
117+ Atan2 ,
118+ Compare ,
119+ Mod ,
120+ Min ,
121+ Max ,
122+ And ,
123+ Or ,
124+ Input ,
125+ Output ,
126+ Load ,
127+ Store ,
128+ }
129+
130+ impl From < RegOp > for BytecodeOp {
131+ fn from ( op : RegOp ) -> Self {
132+ match op {
133+ RegOp :: Input ( ..) => BytecodeOp :: Input ,
134+ RegOp :: Output ( ..) => BytecodeOp :: Output ,
135+ RegOp :: NegReg ( ..) => BytecodeOp :: Neg ,
136+ RegOp :: AbsReg ( ..) => BytecodeOp :: Abs ,
137+ RegOp :: RecipReg ( ..) => BytecodeOp :: Recip ,
138+ RegOp :: SqrtReg ( ..) => BytecodeOp :: Sqrt ,
139+ RegOp :: SquareReg ( ..) => BytecodeOp :: Square ,
140+ RegOp :: FloorReg ( ..) => BytecodeOp :: Floor ,
141+ RegOp :: CeilReg ( ..) => BytecodeOp :: Ceil ,
142+ RegOp :: RoundReg ( ..) => BytecodeOp :: Round ,
143+ RegOp :: SinReg ( ..) => BytecodeOp :: Sin ,
144+ RegOp :: CosReg ( ..) => BytecodeOp :: Cos ,
145+ RegOp :: TanReg ( ..) => BytecodeOp :: Tan ,
146+ RegOp :: AsinReg ( ..) => BytecodeOp :: Asin ,
147+ RegOp :: AcosReg ( ..) => BytecodeOp :: Acos ,
148+ RegOp :: AtanReg ( ..) => BytecodeOp :: Atan ,
149+ RegOp :: ExpReg ( ..) => BytecodeOp :: Exp ,
150+ RegOp :: LnReg ( ..) => BytecodeOp :: Ln ,
151+ RegOp :: NotReg ( ..) => BytecodeOp :: Not ,
152+ RegOp :: Load ( ..) => BytecodeOp :: Load ,
153+ RegOp :: Store ( ..) => BytecodeOp :: Store ,
154+ RegOp :: CopyImm ( ..) | RegOp :: CopyReg ( ..) => BytecodeOp :: Copy ,
155+
156+ RegOp :: AddRegReg ( ..) | RegOp :: AddRegImm ( ..) => BytecodeOp :: Add ,
157+ RegOp :: MulRegReg ( ..) | RegOp :: MulRegImm ( ..) => BytecodeOp :: Mul ,
158+ RegOp :: DivRegReg ( ..)
159+ | RegOp :: DivRegImm ( ..)
160+ | RegOp :: DivImmReg ( ..) => BytecodeOp :: Div ,
161+ RegOp :: SubRegReg ( ..)
162+ | RegOp :: SubRegImm ( ..)
163+ | RegOp :: SubImmReg ( ..) => BytecodeOp :: Sub ,
164+ RegOp :: AtanRegReg ( ..)
165+ | RegOp :: AtanRegImm ( ..)
166+ | RegOp :: AtanImmReg ( ..) => BytecodeOp :: Atan2 ,
167+ RegOp :: MinRegReg ( ..) | RegOp :: MinRegImm ( ..) => BytecodeOp :: Min ,
168+ RegOp :: MaxRegReg ( ..) | RegOp :: MaxRegImm ( ..) => BytecodeOp :: Max ,
169+ RegOp :: CompareRegReg ( ..)
170+ | RegOp :: CompareRegImm ( ..)
171+ | RegOp :: CompareImmReg ( ..) => BytecodeOp :: Compare ,
172+ RegOp :: ModRegReg ( ..)
173+ | RegOp :: ModRegImm ( ..)
174+ | RegOp :: ModImmReg ( ..) => BytecodeOp :: Mod ,
175+ RegOp :: AndRegReg ( ..) | RegOp :: AndRegImm ( ..) => BytecodeOp :: And ,
176+ RegOp :: OrRegReg ( ..) | RegOp :: OrRegImm ( ..) => BytecodeOp :: Or ,
177+ }
178+ }
179+ }
78180
79181/// Serialized bytecode for external evaluation
80182pub struct Bytecode {
@@ -96,6 +198,8 @@ impl Bytecode {
96198 }
97199
98200 /// Maximum register index used by the tape
201+ ///
202+ /// This does not include the virtual register `0xFF` used for immediates
99203 pub fn reg_count ( & self ) -> u8 {
100204 self . reg_count
101205 }
@@ -111,33 +215,42 @@ impl Bytecode {
111215 }
112216
113217 /// Builds a new bytecode object from VM data
114- pub fn new < const N : usize > ( t : & VmData < N > ) -> Self {
218+ ///
219+ /// Returns an error if the reserved register (255) is in use
220+ pub fn new < const N : usize > (
221+ t : & VmData < N > ,
222+ ) -> Result < Self , ReservedRegister > {
115223 // The initial opcode is `OP_JUMP 0x0000_0000`
116224 let mut data = vec ! [ u32 :: MAX , 0u32 ] ;
117225 let mut reg_count = 0u8 ;
118226 let mut mem_count = 0u32 ;
119227 for op in t. iter_asm ( ) {
120- let r = BytecodeOp :: from ( op) ;
121- let mut word = [ r as u8 , 0xFF , 0xFF , 0xFF ] ;
228+ let mut word = [ 0xFF ; 4 ] ;
122229 let mut imm = None ;
123230 let mut store_reg = |i, r| {
124- reg_count = reg_count. max ( r) ; // update the max reg
125- word[ i] = r;
231+ if r == u8:: MAX {
232+ Err ( ReservedRegister )
233+ } else {
234+ reg_count = reg_count. max ( r) ; // update the max reg
235+ word[ i] = r;
236+ Ok ( ( ) )
237+ }
126238 } ;
127239 match op {
128240 RegOp :: Input ( reg, slot) | RegOp :: Output ( reg, slot) => {
129- store_reg ( 1 , reg) ;
241+ store_reg ( 1 , reg) ? ;
130242 imm = Some ( slot) ;
131243 }
132244
133245 RegOp :: Load ( reg, slot) | RegOp :: Store ( reg, slot) => {
134- store_reg ( 1 , reg) ;
246+ store_reg ( 1 , reg) ? ;
135247 mem_count = mem_count. max ( slot) ;
136248 imm = Some ( slot) ;
137249 }
138250
139251 RegOp :: CopyImm ( out, imm_f32) => {
140- store_reg ( 1 , out) ;
252+ store_reg ( 1 , out) ?;
253+ word[ 2 ] = u8:: MAX ;
141254 imm = Some ( imm_f32. to_bits ( ) ) ;
142255 }
143256 RegOp :: NegReg ( out, reg)
@@ -158,28 +271,35 @@ impl Bytecode {
158271 | RegOp :: ExpReg ( out, reg)
159272 | RegOp :: LnReg ( out, reg)
160273 | RegOp :: NotReg ( out, reg) => {
161- store_reg ( 1 , out) ;
162- store_reg ( 2 , reg) ;
274+ store_reg ( 1 , out) ? ;
275+ store_reg ( 2 , reg) ? ;
163276 }
164277
165278 RegOp :: AddRegImm ( out, reg, imm_f32)
166279 | RegOp :: MulRegImm ( out, reg, imm_f32)
167280 | RegOp :: DivRegImm ( out, reg, imm_f32)
168- | RegOp :: DivImmReg ( out, reg, imm_f32)
169- | RegOp :: SubImmReg ( out, reg, imm_f32)
170281 | RegOp :: SubRegImm ( out, reg, imm_f32)
171282 | RegOp :: AtanRegImm ( out, reg, imm_f32)
172- | RegOp :: AtanImmReg ( out, reg, imm_f32)
173283 | RegOp :: MinRegImm ( out, reg, imm_f32)
174284 | RegOp :: MaxRegImm ( out, reg, imm_f32)
175285 | RegOp :: CompareRegImm ( out, reg, imm_f32)
176- | RegOp :: CompareImmReg ( out, reg, imm_f32)
177286 | RegOp :: ModRegImm ( out, reg, imm_f32)
178- | RegOp :: ModImmReg ( out, reg, imm_f32)
179287 | RegOp :: AndRegImm ( out, reg, imm_f32)
180288 | RegOp :: OrRegImm ( out, reg, imm_f32) => {
181- store_reg ( 1 , out) ;
182- store_reg ( 2 , reg) ;
289+ store_reg ( 1 , out) ?;
290+ store_reg ( 2 , reg) ?;
291+ word[ 3 ] = u8:: MAX ;
292+ imm = Some ( imm_f32. to_bits ( ) ) ;
293+ }
294+
295+ RegOp :: DivImmReg ( out, reg, imm_f32)
296+ | RegOp :: SubImmReg ( out, reg, imm_f32)
297+ | RegOp :: AtanImmReg ( out, reg, imm_f32)
298+ | RegOp :: CompareImmReg ( out, reg, imm_f32)
299+ | RegOp :: ModImmReg ( out, reg, imm_f32) => {
300+ store_reg ( 1 , out) ?;
301+ store_reg ( 3 , reg) ?;
302+ word[ 2 ] = u8:: MAX ;
183303 imm = Some ( imm_f32. to_bits ( ) ) ;
184304 }
185305
@@ -194,22 +314,23 @@ impl Bytecode {
194314 | RegOp :: ModRegReg ( out, lhs, rhs)
195315 | RegOp :: AndRegReg ( out, lhs, rhs)
196316 | RegOp :: OrRegReg ( out, lhs, rhs) => {
197- store_reg ( 1 , out) ;
198- store_reg ( 2 , lhs) ;
199- store_reg ( 3 , rhs) ;
317+ store_reg ( 1 , out) ? ;
318+ store_reg ( 2 , lhs) ? ;
319+ store_reg ( 3 , rhs) ? ;
200320 }
201- }
321+ } ;
322+ word[ 0 ] = BytecodeOp :: from ( op) as u8 ;
202323 data. push ( u32:: from_le_bytes ( word) ) ;
203324 data. push ( imm. unwrap_or ( 0xFF000000 ) ) ;
204325 }
205326 // Add the final `OP_JUMP 0xFFFF_FFFF`
206327 data. extend ( [ u32:: MAX , u32:: MAX ] ) ;
207328
208- Bytecode {
329+ Ok ( Bytecode {
209330 data,
210331 mem_count,
211332 reg_count,
212- }
333+ } )
213334 }
214335}
215336
@@ -236,7 +357,7 @@ mod test {
236357 let c = ctx. constant ( 1.0 ) ;
237358 let out = ctx. add ( x, c) . unwrap ( ) ;
238359 let data = VmData :: < 255 > :: new ( & ctx, & [ out] ) . unwrap ( ) ;
239- let bc = Bytecode :: new ( & data) ;
360+ let bc = Bytecode :: new ( & data) . unwrap ( ) ;
240361 let mut iter = bc. data . iter ( ) ;
241362 let mut next = || * iter. next ( ) . unwrap ( ) ;
242363 assert_eq ! ( next( ) , 0xFFFFFFFF ) ; // start marker
@@ -246,10 +367,7 @@ mod test {
246367 [ BytecodeOp :: Input as u8 , 0 , 0xFF , 0xFF ]
247368 ) ;
248369 assert_eq ! ( next( ) , 0 ) ; // input slot 0
249- assert_eq ! (
250- next( ) . to_le_bytes( ) ,
251- [ BytecodeOp :: AddRegImm as u8 , 0 , 0 , 0xFF ]
252- ) ;
370+ assert_eq ! ( next( ) . to_le_bytes( ) , [ BytecodeOp :: Add as u8 , 0 , 0 , 0xFF ] ) ;
253371 assert_eq ! ( f32 :: from_bits( next( ) ) , 1.0 ) ;
254372 assert_eq ! (
255373 next( ) . to_le_bytes( ) ,
0 commit comments