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+ pub enum BytecodeOp {
96+ Copy ,
97+ Neg ,
98+ Abs ,
99+ Recip ,
100+ Sqrt ,
101+ Square ,
102+ Floor ,
103+ Ceil ,
104+ Round ,
105+ Sin ,
106+ Cos ,
107+ Tan ,
108+ Asin ,
109+ Acos ,
110+ Atan ,
111+ Exp ,
112+ Ln ,
113+ Not ,
114+ Add ,
115+ Sub ,
116+ Mul ,
117+ Div ,
118+ Atan2 ,
119+ Compare ,
120+ Mod ,
121+ Min ,
122+ Max ,
123+ And ,
124+ Or ,
125+ Input ,
126+ Output ,
127+ Load ,
128+ Store ,
129+ }
130+
131+ impl From < RegOp > for BytecodeOp {
132+ fn from ( op : RegOp ) -> Self {
133+ match op {
134+ RegOp :: Input ( ..) => BytecodeOp :: Input ,
135+ RegOp :: Output ( ..) => BytecodeOp :: Output ,
136+ RegOp :: NegReg ( ..) => BytecodeOp :: Neg ,
137+ RegOp :: AbsReg ( ..) => BytecodeOp :: Abs ,
138+ RegOp :: RecipReg ( ..) => BytecodeOp :: Recip ,
139+ RegOp :: SqrtReg ( ..) => BytecodeOp :: Sqrt ,
140+ RegOp :: SquareReg ( ..) => BytecodeOp :: Square ,
141+ RegOp :: FloorReg ( ..) => BytecodeOp :: Floor ,
142+ RegOp :: CeilReg ( ..) => BytecodeOp :: Ceil ,
143+ RegOp :: RoundReg ( ..) => BytecodeOp :: Round ,
144+ RegOp :: SinReg ( ..) => BytecodeOp :: Sin ,
145+ RegOp :: CosReg ( ..) => BytecodeOp :: Cos ,
146+ RegOp :: TanReg ( ..) => BytecodeOp :: Tan ,
147+ RegOp :: AsinReg ( ..) => BytecodeOp :: Asin ,
148+ RegOp :: AcosReg ( ..) => BytecodeOp :: Acos ,
149+ RegOp :: AtanReg ( ..) => BytecodeOp :: Atan ,
150+ RegOp :: ExpReg ( ..) => BytecodeOp :: Exp ,
151+ RegOp :: LnReg ( ..) => BytecodeOp :: Ln ,
152+ RegOp :: NotReg ( ..) => BytecodeOp :: Not ,
153+ RegOp :: Load ( ..) => BytecodeOp :: Load ,
154+ RegOp :: Store ( ..) => BytecodeOp :: Store ,
155+ RegOp :: CopyImm ( ..) | RegOp :: CopyReg ( ..) => BytecodeOp :: Copy ,
156+
157+ RegOp :: AddRegReg ( ..) | RegOp :: AddRegImm ( ..) => BytecodeOp :: Add ,
158+ RegOp :: MulRegReg ( ..) | RegOp :: MulRegImm ( ..) => BytecodeOp :: Mul ,
159+ RegOp :: DivRegReg ( ..)
160+ | RegOp :: DivRegImm ( ..)
161+ | RegOp :: DivImmReg ( ..) => BytecodeOp :: Div ,
162+ RegOp :: SubRegReg ( ..)
163+ | RegOp :: SubRegImm ( ..)
164+ | RegOp :: SubImmReg ( ..) => BytecodeOp :: Sub ,
165+ RegOp :: AtanRegReg ( ..)
166+ | RegOp :: AtanRegImm ( ..)
167+ | RegOp :: AtanImmReg ( ..) => BytecodeOp :: Atan2 ,
168+ RegOp :: MinRegReg ( ..) | RegOp :: MinRegImm ( ..) => BytecodeOp :: Min ,
169+ RegOp :: MaxRegReg ( ..) | RegOp :: MaxRegImm ( ..) => BytecodeOp :: Max ,
170+ RegOp :: CompareRegReg ( ..)
171+ | RegOp :: CompareRegImm ( ..)
172+ | RegOp :: CompareImmReg ( ..) => BytecodeOp :: Compare ,
173+ RegOp :: ModRegReg ( ..)
174+ | RegOp :: ModRegImm ( ..)
175+ | RegOp :: ModImmReg ( ..) => BytecodeOp :: Mod ,
176+ RegOp :: AndRegReg ( ..) | RegOp :: AndRegImm ( ..) => BytecodeOp :: And ,
177+ RegOp :: OrRegReg ( ..) | RegOp :: OrRegImm ( ..) => BytecodeOp :: Or ,
178+ }
179+ }
180+ }
78181
79182/// Serialized bytecode for external evaluation
80183pub struct Bytecode {
@@ -96,6 +199,8 @@ impl Bytecode {
96199 }
97200
98201 /// Maximum register index used by the tape
202+ ///
203+ /// This does not include the virtual register `0xFF` used for immediates
99204 pub fn reg_count ( & self ) -> u8 {
100205 self . reg_count
101206 }
@@ -111,33 +216,42 @@ impl Bytecode {
111216 }
112217
113218 /// Builds a new bytecode object from VM data
114- pub fn new < const N : usize > ( t : & VmData < N > ) -> Self {
219+ ///
220+ /// Returns an error if the reserved register (255) is in use
221+ pub fn new < const N : usize > (
222+ t : & VmData < N > ,
223+ ) -> Result < Self , ReservedRegister > {
115224 // The initial opcode is `OP_JUMP 0x0000_0000`
116225 let mut data = vec ! [ u32 :: MAX , 0u32 ] ;
117226 let mut reg_count = 0u8 ;
118227 let mut mem_count = 0u32 ;
119228 for op in t. iter_asm ( ) {
120- let r = BytecodeOp :: from ( op) ;
121- let mut word = [ r as u8 , 0xFF , 0xFF , 0xFF ] ;
229+ let mut word = [ 0xFF ; 4 ] ;
122230 let mut imm = None ;
123231 let mut store_reg = |i, r| {
124- reg_count = reg_count. max ( r) ; // update the max reg
125- word[ i] = r;
232+ if r == u8:: MAX {
233+ Err ( ReservedRegister )
234+ } else {
235+ reg_count = reg_count. max ( r) ; // update the max reg
236+ word[ i] = r;
237+ Ok ( ( ) )
238+ }
126239 } ;
127240 match op {
128241 RegOp :: Input ( reg, slot) | RegOp :: Output ( reg, slot) => {
129- store_reg ( 1 , reg) ;
242+ store_reg ( 1 , reg) ? ;
130243 imm = Some ( slot) ;
131244 }
132245
133246 RegOp :: Load ( reg, slot) | RegOp :: Store ( reg, slot) => {
134- store_reg ( 1 , reg) ;
247+ store_reg ( 1 , reg) ? ;
135248 mem_count = mem_count. max ( slot) ;
136249 imm = Some ( slot) ;
137250 }
138251
139252 RegOp :: CopyImm ( out, imm_f32) => {
140- store_reg ( 1 , out) ;
253+ store_reg ( 1 , out) ?;
254+ word[ 2 ] = u8:: MAX ;
141255 imm = Some ( imm_f32. to_bits ( ) ) ;
142256 }
143257 RegOp :: NegReg ( out, reg)
@@ -158,28 +272,35 @@ impl Bytecode {
158272 | RegOp :: ExpReg ( out, reg)
159273 | RegOp :: LnReg ( out, reg)
160274 | RegOp :: NotReg ( out, reg) => {
161- store_reg ( 1 , out) ;
162- store_reg ( 2 , reg) ;
275+ store_reg ( 1 , out) ? ;
276+ store_reg ( 2 , reg) ? ;
163277 }
164278
165279 RegOp :: AddRegImm ( out, reg, imm_f32)
166280 | RegOp :: MulRegImm ( out, reg, imm_f32)
167281 | RegOp :: DivRegImm ( out, reg, imm_f32)
168- | RegOp :: DivImmReg ( out, reg, imm_f32)
169- | RegOp :: SubImmReg ( out, reg, imm_f32)
170282 | RegOp :: SubRegImm ( out, reg, imm_f32)
171283 | RegOp :: AtanRegImm ( out, reg, imm_f32)
172- | RegOp :: AtanImmReg ( out, reg, imm_f32)
173284 | RegOp :: MinRegImm ( out, reg, imm_f32)
174285 | RegOp :: MaxRegImm ( out, reg, imm_f32)
175286 | RegOp :: CompareRegImm ( out, reg, imm_f32)
176- | RegOp :: CompareImmReg ( out, reg, imm_f32)
177287 | RegOp :: ModRegImm ( out, reg, imm_f32)
178- | RegOp :: ModImmReg ( out, reg, imm_f32)
179288 | RegOp :: AndRegImm ( out, reg, imm_f32)
180289 | RegOp :: OrRegImm ( out, reg, imm_f32) => {
181- store_reg ( 1 , out) ;
182- store_reg ( 2 , reg) ;
290+ store_reg ( 1 , out) ?;
291+ store_reg ( 2 , reg) ?;
292+ word[ 3 ] = u8:: MAX ;
293+ imm = Some ( imm_f32. to_bits ( ) ) ;
294+ }
295+
296+ RegOp :: DivImmReg ( out, reg, imm_f32)
297+ | RegOp :: SubImmReg ( out, reg, imm_f32)
298+ | RegOp :: AtanImmReg ( out, reg, imm_f32)
299+ | RegOp :: CompareImmReg ( out, reg, imm_f32)
300+ | RegOp :: ModImmReg ( out, reg, imm_f32) => {
301+ store_reg ( 1 , out) ?;
302+ store_reg ( 3 , reg) ?;
303+ word[ 2 ] = u8:: MAX ;
183304 imm = Some ( imm_f32. to_bits ( ) ) ;
184305 }
185306
@@ -194,22 +315,23 @@ impl Bytecode {
194315 | RegOp :: ModRegReg ( out, lhs, rhs)
195316 | RegOp :: AndRegReg ( out, lhs, rhs)
196317 | RegOp :: OrRegReg ( out, lhs, rhs) => {
197- store_reg ( 1 , out) ;
198- store_reg ( 2 , lhs) ;
199- store_reg ( 3 , rhs) ;
318+ store_reg ( 1 , out) ? ;
319+ store_reg ( 2 , lhs) ? ;
320+ store_reg ( 3 , rhs) ? ;
200321 }
201- }
322+ } ;
323+ word[ 0 ] = BytecodeOp :: from ( op) as u8 ;
202324 data. push ( u32:: from_le_bytes ( word) ) ;
203325 data. push ( imm. unwrap_or ( 0xFF000000 ) ) ;
204326 }
205327 // Add the final `OP_JUMP 0xFFFF_FFFF`
206328 data. extend ( [ u32:: MAX , u32:: MAX ] ) ;
207329
208- Bytecode {
330+ Ok ( Bytecode {
209331 data,
210332 mem_count,
211333 reg_count,
212- }
334+ } )
213335 }
214336}
215337
@@ -236,7 +358,7 @@ mod test {
236358 let c = ctx. constant ( 1.0 ) ;
237359 let out = ctx. add ( x, c) . unwrap ( ) ;
238360 let data = VmData :: < 255 > :: new ( & ctx, & [ out] ) . unwrap ( ) ;
239- let bc = Bytecode :: new ( & data) ;
361+ let bc = Bytecode :: new ( & data) . unwrap ( ) ;
240362 let mut iter = bc. data . iter ( ) ;
241363 let mut next = || * iter. next ( ) . unwrap ( ) ;
242364 assert_eq ! ( next( ) , 0xFFFFFFFF ) ; // start marker
@@ -246,10 +368,7 @@ mod test {
246368 [ BytecodeOp :: Input as u8 , 0 , 0xFF , 0xFF ]
247369 ) ;
248370 assert_eq ! ( next( ) , 0 ) ; // input slot 0
249- assert_eq ! (
250- next( ) . to_le_bytes( ) ,
251- [ BytecodeOp :: AddRegImm as u8 , 0 , 0 , 0xFF ]
252- ) ;
371+ assert_eq ! ( next( ) . to_le_bytes( ) , [ BytecodeOp :: Add as u8 , 0 , 0 , 0xFF ] ) ;
253372 assert_eq ! ( f32 :: from_bits( next( ) ) , 1.0 ) ;
254373 assert_eq ! (
255374 next( ) . to_le_bytes( ) ,
0 commit comments