6767//!
6868//! ## Opcode values
6969//!
70- //! Opcode values are generated automatically from [`BytecodeOp`]
71- //! values, which are one-to-one with [`RegOp`] variants.
70+ //! Opcode values are generated automatically from [`BytecodeOp`] tags.
7271#![ warn( missing_docs) ]
7372
7473use fidget_core:: { compiler:: RegOp , vm:: VmData } ;
7574use zerocopy:: IntoBytes ;
7675
77- pub use fidget_core:: compiler:: RegOpDiscriminants as BytecodeOp ;
76+ /// Error type for bytecode builder
77+ #[ derive( thiserror:: Error , Debug , PartialEq ) ]
78+ #[ error( "register 255 is reserved" ) ]
79+ pub struct ReservedRegister ;
80+
81+ /// Operations in the bytecode tape
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+ #[ expect( missing_docs) ]
95+ #[ repr( u8 ) ]
96+ pub enum BytecodeOp {
97+ Copy ,
98+ Neg ,
99+ Abs ,
100+ Recip ,
101+ Sqrt ,
102+ Square ,
103+ Floor ,
104+ Ceil ,
105+ Round ,
106+ Sin ,
107+ Cos ,
108+ Tan ,
109+ Asin ,
110+ Acos ,
111+ Atan ,
112+ Exp ,
113+ Ln ,
114+ Not ,
115+ Add ,
116+ Sub ,
117+ Mul ,
118+ Div ,
119+ Atan2 ,
120+ Compare ,
121+ Mod ,
122+ Min ,
123+ Max ,
124+ And ,
125+ Or ,
126+ Input ,
127+ Output ,
128+ Load ,
129+ Store ,
130+ }
131+
132+ impl From < RegOp > for BytecodeOp {
133+ fn from ( op : RegOp ) -> Self {
134+ match op {
135+ RegOp :: Input ( ..) => BytecodeOp :: Input ,
136+ RegOp :: Output ( ..) => BytecodeOp :: Output ,
137+ RegOp :: NegReg ( ..) => BytecodeOp :: Neg ,
138+ RegOp :: AbsReg ( ..) => BytecodeOp :: Abs ,
139+ RegOp :: RecipReg ( ..) => BytecodeOp :: Recip ,
140+ RegOp :: SqrtReg ( ..) => BytecodeOp :: Sqrt ,
141+ RegOp :: SquareReg ( ..) => BytecodeOp :: Square ,
142+ RegOp :: FloorReg ( ..) => BytecodeOp :: Floor ,
143+ RegOp :: CeilReg ( ..) => BytecodeOp :: Ceil ,
144+ RegOp :: RoundReg ( ..) => BytecodeOp :: Round ,
145+ RegOp :: SinReg ( ..) => BytecodeOp :: Sin ,
146+ RegOp :: CosReg ( ..) => BytecodeOp :: Cos ,
147+ RegOp :: TanReg ( ..) => BytecodeOp :: Tan ,
148+ RegOp :: AsinReg ( ..) => BytecodeOp :: Asin ,
149+ RegOp :: AcosReg ( ..) => BytecodeOp :: Acos ,
150+ RegOp :: AtanReg ( ..) => BytecodeOp :: Atan ,
151+ RegOp :: ExpReg ( ..) => BytecodeOp :: Exp ,
152+ RegOp :: LnReg ( ..) => BytecodeOp :: Ln ,
153+ RegOp :: NotReg ( ..) => BytecodeOp :: Not ,
154+ RegOp :: Load ( ..) => BytecodeOp :: Load ,
155+ RegOp :: Store ( ..) => BytecodeOp :: Store ,
156+ RegOp :: CopyImm ( ..) | RegOp :: CopyReg ( ..) => BytecodeOp :: Copy ,
157+
158+ RegOp :: AddRegReg ( ..) | RegOp :: AddRegImm ( ..) => BytecodeOp :: Add ,
159+ RegOp :: MulRegReg ( ..) | RegOp :: MulRegImm ( ..) => BytecodeOp :: Mul ,
160+ RegOp :: DivRegReg ( ..)
161+ | RegOp :: DivRegImm ( ..)
162+ | RegOp :: DivImmReg ( ..) => BytecodeOp :: Div ,
163+ RegOp :: SubRegReg ( ..)
164+ | RegOp :: SubRegImm ( ..)
165+ | RegOp :: SubImmReg ( ..) => BytecodeOp :: Sub ,
166+ RegOp :: AtanRegReg ( ..)
167+ | RegOp :: AtanRegImm ( ..)
168+ | RegOp :: AtanImmReg ( ..) => BytecodeOp :: Atan2 ,
169+ RegOp :: MinRegReg ( ..) | RegOp :: MinRegImm ( ..) => BytecodeOp :: Min ,
170+ RegOp :: MaxRegReg ( ..) | RegOp :: MaxRegImm ( ..) => BytecodeOp :: Max ,
171+ RegOp :: CompareRegReg ( ..)
172+ | RegOp :: CompareRegImm ( ..)
173+ | RegOp :: CompareImmReg ( ..) => BytecodeOp :: Compare ,
174+ RegOp :: ModRegReg ( ..)
175+ | RegOp :: ModRegImm ( ..)
176+ | RegOp :: ModImmReg ( ..) => BytecodeOp :: Mod ,
177+ RegOp :: AndRegReg ( ..) | RegOp :: AndRegImm ( ..) => BytecodeOp :: And ,
178+ RegOp :: OrRegReg ( ..) | RegOp :: OrRegImm ( ..) => BytecodeOp :: Or ,
179+ }
180+ }
181+ }
78182
79183/// Serialized bytecode for external evaluation
80184pub struct Bytecode {
@@ -96,6 +200,8 @@ impl Bytecode {
96200 }
97201
98202 /// Maximum register index used by the tape
203+ ///
204+ /// This does not include the virtual register `0xFF` used for immediates
99205 pub fn reg_count ( & self ) -> u8 {
100206 self . reg_count
101207 }
@@ -111,33 +217,42 @@ impl Bytecode {
111217 }
112218
113219 /// Builds a new bytecode object from VM data
114- pub fn new < const N : usize > ( t : & VmData < N > ) -> Self {
220+ ///
221+ /// Returns an error if the reserved register (255) is in use
222+ pub fn new < const N : usize > (
223+ t : & VmData < N > ,
224+ ) -> Result < Self , ReservedRegister > {
115225 // The initial opcode is `OP_JUMP 0x0000_0000`
116226 let mut data = vec ! [ u32 :: MAX , 0u32 ] ;
117227 let mut reg_count = 0u8 ;
118228 let mut mem_count = 0u32 ;
119229 for op in t. iter_asm ( ) {
120- let r = BytecodeOp :: from ( op) ;
121- let mut word = [ r as u8 , 0xFF , 0xFF , 0xFF ] ;
230+ let mut word = [ 0xFF ; 4 ] ;
122231 let mut imm = None ;
123232 let mut store_reg = |i, r| {
124- reg_count = reg_count. max ( r) ; // update the max reg
125- word[ i] = r;
233+ if r == u8:: MAX {
234+ Err ( ReservedRegister )
235+ } else {
236+ reg_count = reg_count. max ( r) ; // update the max reg
237+ word[ i] = r;
238+ Ok ( ( ) )
239+ }
126240 } ;
127241 match op {
128242 RegOp :: Input ( reg, slot) | RegOp :: Output ( reg, slot) => {
129- store_reg ( 1 , reg) ;
243+ store_reg ( 1 , reg) ? ;
130244 imm = Some ( slot) ;
131245 }
132246
133247 RegOp :: Load ( reg, slot) | RegOp :: Store ( reg, slot) => {
134- store_reg ( 1 , reg) ;
248+ store_reg ( 1 , reg) ? ;
135249 mem_count = mem_count. max ( slot) ;
136250 imm = Some ( slot) ;
137251 }
138252
139253 RegOp :: CopyImm ( out, imm_f32) => {
140- store_reg ( 1 , out) ;
254+ store_reg ( 1 , out) ?;
255+ word[ 2 ] = u8:: MAX ;
141256 imm = Some ( imm_f32. to_bits ( ) ) ;
142257 }
143258 RegOp :: NegReg ( out, reg)
@@ -158,28 +273,35 @@ impl Bytecode {
158273 | RegOp :: ExpReg ( out, reg)
159274 | RegOp :: LnReg ( out, reg)
160275 | RegOp :: NotReg ( out, reg) => {
161- store_reg ( 1 , out) ;
162- store_reg ( 2 , reg) ;
276+ store_reg ( 1 , out) ? ;
277+ store_reg ( 2 , reg) ? ;
163278 }
164279
165280 RegOp :: AddRegImm ( out, reg, imm_f32)
166281 | RegOp :: MulRegImm ( out, reg, imm_f32)
167282 | RegOp :: DivRegImm ( out, reg, imm_f32)
168- | RegOp :: DivImmReg ( out, reg, imm_f32)
169- | RegOp :: SubImmReg ( out, reg, imm_f32)
170283 | RegOp :: SubRegImm ( out, reg, imm_f32)
171284 | RegOp :: AtanRegImm ( out, reg, imm_f32)
172- | RegOp :: AtanImmReg ( out, reg, imm_f32)
173285 | RegOp :: MinRegImm ( out, reg, imm_f32)
174286 | RegOp :: MaxRegImm ( out, reg, imm_f32)
175287 | RegOp :: CompareRegImm ( out, reg, imm_f32)
176- | RegOp :: CompareImmReg ( out, reg, imm_f32)
177288 | RegOp :: ModRegImm ( out, reg, imm_f32)
178- | RegOp :: ModImmReg ( out, reg, imm_f32)
179289 | RegOp :: AndRegImm ( out, reg, imm_f32)
180290 | RegOp :: OrRegImm ( out, reg, imm_f32) => {
181- store_reg ( 1 , out) ;
182- store_reg ( 2 , reg) ;
291+ store_reg ( 1 , out) ?;
292+ store_reg ( 2 , reg) ?;
293+ word[ 3 ] = u8:: MAX ;
294+ imm = Some ( imm_f32. to_bits ( ) ) ;
295+ }
296+
297+ RegOp :: DivImmReg ( out, reg, imm_f32)
298+ | RegOp :: SubImmReg ( out, reg, imm_f32)
299+ | RegOp :: AtanImmReg ( out, reg, imm_f32)
300+ | RegOp :: CompareImmReg ( out, reg, imm_f32)
301+ | RegOp :: ModImmReg ( out, reg, imm_f32) => {
302+ store_reg ( 1 , out) ?;
303+ store_reg ( 3 , reg) ?;
304+ word[ 2 ] = u8:: MAX ;
183305 imm = Some ( imm_f32. to_bits ( ) ) ;
184306 }
185307
@@ -194,22 +316,23 @@ impl Bytecode {
194316 | RegOp :: ModRegReg ( out, lhs, rhs)
195317 | RegOp :: AndRegReg ( out, lhs, rhs)
196318 | RegOp :: OrRegReg ( out, lhs, rhs) => {
197- store_reg ( 1 , out) ;
198- store_reg ( 2 , lhs) ;
199- store_reg ( 3 , rhs) ;
319+ store_reg ( 1 , out) ? ;
320+ store_reg ( 2 , lhs) ? ;
321+ store_reg ( 3 , rhs) ? ;
200322 }
201- }
323+ } ;
324+ word[ 0 ] = BytecodeOp :: from ( op) as u8 ;
202325 data. push ( u32:: from_le_bytes ( word) ) ;
203326 data. push ( imm. unwrap_or ( 0xFF000000 ) ) ;
204327 }
205328 // Add the final `OP_JUMP 0xFFFF_FFFF`
206329 data. extend ( [ u32:: MAX , u32:: MAX ] ) ;
207330
208- Bytecode {
331+ Ok ( Bytecode {
209332 data,
210333 mem_count,
211334 reg_count,
212- }
335+ } )
213336 }
214337}
215338
@@ -236,7 +359,7 @@ mod test {
236359 let c = ctx. constant ( 1.0 ) ;
237360 let out = ctx. add ( x, c) . unwrap ( ) ;
238361 let data = VmData :: < 255 > :: new ( & ctx, & [ out] ) . unwrap ( ) ;
239- let bc = Bytecode :: new ( & data) ;
362+ let bc = Bytecode :: new ( & data) . unwrap ( ) ;
240363 let mut iter = bc. data . iter ( ) ;
241364 let mut next = || * iter. next ( ) . unwrap ( ) ;
242365 assert_eq ! ( next( ) , 0xFFFFFFFF ) ; // start marker
@@ -246,10 +369,7 @@ mod test {
246369 [ BytecodeOp :: Input as u8 , 0 , 0xFF , 0xFF ]
247370 ) ;
248371 assert_eq ! ( next( ) , 0 ) ; // input slot 0
249- assert_eq ! (
250- next( ) . to_le_bytes( ) ,
251- [ BytecodeOp :: AddRegImm as u8 , 0 , 0 , 0xFF ]
252- ) ;
372+ assert_eq ! ( next( ) . to_le_bytes( ) , [ BytecodeOp :: Add as u8 , 0 , 0 , 0xFF ] ) ;
253373 assert_eq ! ( f32 :: from_bits( next( ) ) , 1.0 ) ;
254374 assert_eq ! (
255375 next( ) . to_le_bytes( ) ,
0 commit comments