11use crate :: config:: Config ;
22use anyhow:: Result ;
3- use arbitrary:: Unstructured ;
3+ use arbitrary:: { Arbitrary , Unstructured } ;
44use cranelift:: codegen:: ir:: types:: * ;
55use cranelift:: codegen:: ir:: {
66 AbiParam , Block , ExternalName , Function , Opcode , Signature , Type , Value ,
77} ;
88use cranelift:: codegen:: isa:: CallConv ;
99use cranelift:: frontend:: { FunctionBuilder , FunctionBuilderContext , Variable } ;
10- use cranelift:: prelude:: { EntityRef , InstBuilder } ;
10+ use cranelift:: prelude:: { EntityRef , InstBuilder , IntCC } ;
1111
1212type BlockSignature = Vec < Type > ;
1313
@@ -117,11 +117,30 @@ where
117117 Ok ( CallConv :: SystemV )
118118 }
119119
120+ fn generate_intcc ( & mut self ) -> Result < IntCC > {
121+ Ok ( * self . u . choose (
122+ & [
123+ IntCC :: Equal ,
124+ IntCC :: NotEqual ,
125+ IntCC :: SignedLessThan ,
126+ IntCC :: SignedGreaterThanOrEqual ,
127+ IntCC :: SignedGreaterThan ,
128+ IntCC :: SignedLessThanOrEqual ,
129+ IntCC :: UnsignedLessThan ,
130+ IntCC :: UnsignedGreaterThanOrEqual ,
131+ IntCC :: UnsignedGreaterThan ,
132+ IntCC :: UnsignedLessThanOrEqual ,
133+ IntCC :: Overflow ,
134+ IntCC :: NotOverflow ,
135+ ] [ ..] ,
136+ ) ?)
137+ }
138+
120139 fn generate_type ( & mut self ) -> Result < Type > {
121140 // TODO: It would be nice if we could get these directly from cranelift
122141 let scalars = [
123142 // IFLAGS, FFLAGS,
124- // B1, B8, B16, B32, B64, B128,
143+ B1 , // B8, B16, B32, B64, B128,
125144 I8 , I16 , I32 , I64 ,
126145 // I128,
127146 // F32, F64,
@@ -180,60 +199,140 @@ where
180199
181200 /// Generates an instruction(`iconst`/`fconst`/etc...) to introduce a constant value
182201 fn generate_const ( & mut self , builder : & mut FunctionBuilder , ty : Type ) -> Result < Value > {
183- let imm64 = match ty {
184- I8 => self . u . arbitrary :: < i8 > ( ) ? as i64 ,
185- I16 => self . u . arbitrary :: < i16 > ( ) ? as i64 ,
186- I32 => self . u . arbitrary :: < i32 > ( ) ? as i64 ,
187- I64 => self . u . arbitrary :: < i64 > ( ) ?,
188- _ => unreachable ! ( ) ,
189- } ;
190- let val = builder. ins ( ) . iconst ( ty, imm64) ;
191-
192- Ok ( val)
202+ Ok ( match ty {
203+ ty if ty. is_int ( ) => {
204+ let imm64 = match ty {
205+ I8 => self . u . arbitrary :: < i8 > ( ) ? as i64 ,
206+ I16 => self . u . arbitrary :: < i16 > ( ) ? as i64 ,
207+ I32 => self . u . arbitrary :: < i32 > ( ) ? as i64 ,
208+ I64 => self . u . arbitrary :: < i64 > ( ) ?,
209+ _ => unreachable ! ( ) ,
210+ } ;
211+ builder. ins ( ) . iconst ( ty, imm64)
212+ }
213+ ty if ty. is_bool ( ) => builder. ins ( ) . bconst ( B1 , bool:: arbitrary ( self . u ) ?) ,
214+ _ => unimplemented ! ( ) ,
215+ } )
193216 }
194217
195- fn generate_return ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
196- let ret_params = builder. func . signature . returns . clone ( ) ;
218+ /// Chooses a random block which can be targeted by a jump / branch.
219+ /// This means any block that is not the first block.
220+ ///
221+ /// For convenience we also generate values that match the block's signature
222+ fn generate_target_block (
223+ & mut self ,
224+ builder : & mut FunctionBuilder ,
225+ ) -> Result < ( Block , Vec < Value > ) > {
226+ let block_targets = & self . blocks [ 1 ..] ;
227+ let ( block, signature) = self . u . choose ( block_targets) ?. clone ( ) ;
228+ let args = self . generate_values_for_signature ( builder, signature. into_iter ( ) ) ?;
229+ Ok ( ( block, args) )
230+ }
197231
198- let vars = ret_params
199- . iter ( )
200- . map ( |p| self . get_variable_of_type ( p. value_type ) )
201- . collect :: < Result < Vec < _ > > > ( ) ?;
232+ fn generate_values_for_signature < I : Iterator < Item = Type > > (
233+ & mut self ,
234+ builder : & mut FunctionBuilder ,
235+ signature : I ,
236+ ) -> Result < Vec < Value > > {
237+ signature
238+ . map ( |ty| {
239+ let var = self . get_variable_of_type ( ty) ?;
240+ let val = builder. use_var ( var) ;
241+ Ok ( val)
242+ } )
243+ . collect ( )
244+ }
202245
203- let vals = vars
204- . into_iter ( )
205- . map ( |v| builder. use_var ( v) )
206- . collect :: < Vec < _ > > ( ) ;
246+ fn generate_return ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
247+ let types: Vec < Type > = {
248+ let rets = & builder. func . signature . returns ;
249+ rets. iter ( ) . map ( |p| p. value_type ) . collect ( )
250+ } ;
251+ let vals = self . generate_values_for_signature ( builder, types. into_iter ( ) ) ?;
207252
208253 builder. ins ( ) . return_ ( & vals[ ..] ) ;
209254 Ok ( ( ) )
210255 }
211256
212257 fn generate_jump ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
213- let ( block, signature) = {
214- let target = self . u . choose ( & self . blocks [ ..] ) ?;
215- let target = target. clone ( ) ;
216- target
217- } ;
258+ let ( block, args) = self . generate_target_block ( builder) ?;
259+ builder. ins ( ) . jump ( block, & args[ ..] ) ;
260+ Ok ( ( ) )
261+ }
218262
219- let vars = signature
220- . iter ( )
221- . map ( |ty| self . get_variable_of_type ( * ty) )
222- . collect :: < Result < Vec < _ > > > ( ) ?;
263+ /// Generates a brz/brnz into a random block
264+ fn generate_br ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
265+ let ( block, args) = self . generate_target_block ( builder) ?;
223266
224- let vals = vars
225- . into_iter ( )
226- . map ( |v| builder. use_var ( v) )
227- . collect :: < Vec < _ > > ( ) ;
267+ let condbr_types = [
268+ I8 , I16 , I32 , I64 , // TODO: I128
269+ B1 ,
270+ ] ;
271+ let _type = * self . u . choose ( & condbr_types[ ..] ) ?;
272+ let var = self . get_variable_of_type ( _type) ?;
273+ let val = builder. use_var ( var) ;
274+
275+ if bool:: arbitrary ( self . u ) ? {
276+ builder. ins ( ) . brz ( val, block, & args[ ..] ) ;
277+ } else {
278+ builder. ins ( ) . brnz ( val, block, & args[ ..] ) ;
279+ }
228280
229- builder. ins ( ) . jump ( * block, & vals[ ..] ) ;
281+ // After brz/brnz we must generate a jump
282+ self . generate_jump ( builder) ?;
230283 Ok ( ( ) )
231284 }
232285
233- /// Inserts a random instruction into the block
234- fn generate_instruction ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
235- let ( op, args, rets, inserter) = * self . u . choose ( OPCODE_SIGNATURES ) ?;
236- inserter ( self , builder, op, args, rets)
286+ fn generate_bricmp ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
287+ let ( block, args) = self . generate_target_block ( builder) ?;
288+ let cond = self . generate_intcc ( ) ?;
289+
290+ let bricmp_types = [
291+ I8 , I16 , I32 , I64 , // TODO: I128
292+ ] ;
293+ let _type = * self . u . choose ( & bricmp_types[ ..] ) ?;
294+
295+ let lhs_var = self . get_variable_of_type ( _type) ?;
296+ let lhs_val = builder. use_var ( lhs_var) ;
297+
298+ let rhs_var = self . get_variable_of_type ( _type) ?;
299+ let rhs_val = builder. use_var ( rhs_var) ;
300+
301+ builder
302+ . ins ( )
303+ . br_icmp ( cond, lhs_val, rhs_val, block, & args[ ..] ) ;
304+
305+ // After bricmp's we must generate a jump
306+ self . generate_jump ( builder) ?;
307+ Ok ( ( ) )
308+ }
309+
310+ /// We always need to exit safely out of a block.
311+ /// This either means a jump into another block or a return.
312+ fn finalize_block ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
313+ let gen = self . u . choose (
314+ & [
315+ Self :: generate_bricmp,
316+ Self :: generate_br,
317+ Self :: generate_jump,
318+ Self :: generate_return,
319+ ] [ ..] ,
320+ ) ?;
321+
322+ gen ( self , builder)
323+ }
324+
325+ /// Fills the current block with random instructions
326+ fn generate_instructions ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
327+ for _ in 0 ..self
328+ . u
329+ . int_in_range ( self . config . instructions_per_block . clone ( ) ) ?
330+ {
331+ let ( op, args, rets, inserter) = * self . u . choose ( OPCODE_SIGNATURES ) ?;
332+ inserter ( self , builder, op, args, rets) ?;
333+ }
334+
335+ Ok ( ( ) )
237336 }
238337
239338 /// Creates a random amount of blocks in this function
@@ -260,7 +359,11 @@ where
260359 builder. append_block_params_for_function_params ( block) ;
261360 Ok ( ( block, sig. params . iter ( ) . map ( |a| a. value_type ) . collect ( ) ) )
262361 } else {
263- Ok ( ( block, self . generate_block_signature ( ) ?) )
362+ let sig = self . generate_block_signature ( ) ?;
363+ sig. iter ( ) . for_each ( |ty| {
364+ builder. append_block_param ( block, * ty) ;
365+ } ) ;
366+ Ok ( ( block, sig) )
264367 }
265368 } )
266369 . collect :: < Result < Vec < _ > > > ( ) ?;
@@ -280,7 +383,29 @@ where
280383 Ok ( params)
281384 }
282385
283- /// We generate a function in multiple stages: by first creating a random number of empty
386+ fn build_variable_pool ( & mut self , builder : & mut FunctionBuilder ) -> Result < ( ) > {
387+ let block = builder. current_block ( ) . unwrap ( ) ;
388+ let func_params = builder. func . signature . params . clone ( ) ;
389+
390+ // Define variables for the function signature
391+ for ( i, param) in func_params. iter ( ) . enumerate ( ) {
392+ let var = self . create_var ( builder, param. value_type ) ?;
393+ let block_param = builder. block_params ( block) [ i] ;
394+ builder. def_var ( var, block_param) ;
395+ }
396+
397+ // Create a pool of vars that are going to be used in this function
398+ for _ in 0 ..self . u . int_in_range ( self . config . vars_per_function . clone ( ) ) ? {
399+ let ty = self . generate_type ( ) ?;
400+ let var = self . create_var ( builder, ty) ?;
401+ let value = self . generate_const ( builder, ty) ?;
402+ builder. def_var ( var, value) ;
403+ }
404+
405+ Ok ( ( ) )
406+ }
407+
408+ /// We generate a function in multiple stages:
284409 ///
285410 /// * First we generate a random number of empty blocks
286411 /// * Then we generate a random pool of variables to be used throughout the function
@@ -299,50 +424,28 @@ where
299424 self . blocks = self . generate_blocks ( & mut builder, & sig) ?;
300425
301426 // Main instruction generation loop
302- for ( i, ( block, signature) ) in self . blocks . clone ( ) . iter ( ) . enumerate ( ) {
427+ for ( i, ( block, block_sig) ) in self . blocks . clone ( ) . iter ( ) . enumerate ( ) {
428+ let is_block0 = i == 0 ;
303429 builder. switch_to_block ( * block) ;
304430
305- if i == 0 {
306- // Define variables for the function signature
307- for ( i, param) in sig. params . iter ( ) . enumerate ( ) {
308- let var = self . create_var ( & mut builder, param. value_type ) ?;
309- let block_param = builder. block_params ( * block) [ i] ;
310- builder. def_var ( var, block_param) ;
311- }
312-
313- // Create a pool of vars that are going to be used in this function
314- for _ in 0 ..self . u . int_in_range ( self . config . vars_per_function . clone ( ) ) ? {
315- let ty = self . generate_type ( ) ?;
316- let var = self . create_var ( & mut builder, ty) ?;
317- let value = self . generate_const ( & mut builder, ty) ?;
318- builder. def_var ( var, value) ;
319- }
431+ if is_block0 {
432+ // The first block is special because we must create variables both for the
433+ // block signature and for the variable pool. Additionally, we must also define
434+ // initial values for all variables that are not the function signature.
435+ self . build_variable_pool ( & mut builder) ?;
320436 } else {
321437 // Define variables for the block params
322- for ( i, ty) in signature . iter ( ) . enumerate ( ) {
438+ for ( i, ty) in block_sig . iter ( ) . enumerate ( ) {
323439 let var = self . get_variable_of_type ( * ty) ?;
324440 let block_param = builder. block_params ( * block) [ i] ;
325441 builder. def_var ( var, block_param) ;
326442 }
327443 }
328444
329445 // Generate block instructions
330- for _ in 0 ..self
331- . u
332- . int_in_range ( self . config . instructions_per_block . clone ( ) ) ?
333- {
334- self . generate_instruction ( & mut builder) ?;
335- }
446+ self . generate_instructions ( & mut builder) ?;
336447
337- // We always need to exit safely out of a block.
338- // For block 0 this means a return, but for other block it is a jump into any other
339- // random block.
340- if i == 0 {
341- // TODO: We should make this, part of the regular instruction selection
342- self . generate_return ( & mut builder) ?;
343- } else {
344- self . generate_jump ( & mut builder) ?;
345- }
448+ self . finalize_block ( & mut builder) ?;
346449 }
347450
348451 builder. seal_all_blocks ( ) ;
0 commit comments