use llvm_native_core::module::Module;
use llvm_native_core::types::{Type, TypeKind};
use llvm_native_core::value::{valref, SubclassKind, Value, ValueRef};
use std::rc::Rc;
pub struct LLVMStress {
state: u64,
pub modules_generated: usize,
pub functions_generated: usize,
}
impl LLVMStress {
pub fn new() -> Self {
Self {
state: 0x8a5cd789635d2dff,
modules_generated: 0,
functions_generated: 0,
}
}
pub fn generate_module(&mut self, seed: u64) -> Module {
self.state = seed
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(0xbf58476d1ce4e5b9);
self.modules_generated += 1;
let mut module = Module::new(&format!("stress_{}", seed));
let num_types = (self.next_rand() % 6) + 1;
for _ in 0..num_types {
let ty = self.generate_random_type();
module.types.push(ty);
}
let num_funcs = (self.next_rand() % 5) + 1;
for _ in 0..num_funcs {
let func = self.generate_random_function();
module.functions.push(func);
self.functions_generated += 1;
}
module
}
fn next_rand(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
fn next_rand_mod(&mut self, max: u64) -> u64 {
if max == 0 {
return 0;
}
self.next_rand() % max
}
fn generate_random_type(&mut self) -> Type {
let choice = self.next_rand_mod(8);
match choice {
0 => Type::i1(),
1 => Type::i8(),
2 => Type::i32(),
3 => Type::i64(),
4 => Type::float(),
5 => Type::double(),
6 => Type::pointer(0),
7 => {
let elem = Type::i32();
Type::array_with(4, elem.id)
}
_ => Type::i32(),
}
}
fn generate_random_function(&mut self) -> ValueRef {
let func_name = format!("func_{}", self.next_rand_mod(10000));
let ret_ty = self.generate_random_type();
let num_params = self.next_rand_mod(5) as usize;
let mut param_types = Vec::new();
for _ in 0..num_params {
param_types.push(self.generate_random_type());
}
llvm_native_core::function::new_function(&func_name, ret_ty, ¶m_types)
}
fn generate_random_instruction(&mut self, ty: &Type, _operands: &[ValueRef]) -> ValueRef {
let choice = self.next_rand_mod(12);
match choice {
0 => self.gen_alloca(),
1 => self.gen_load(ty),
2 => self.gen_add(ty),
3 => self.gen_sub(ty),
4 => self.gen_mul(ty),
5 => self.gen_and(ty),
6 => self.gen_or(ty),
7 => self.gen_xor(ty),
8 => self.gen_icmp(),
9 => self.gen_call(),
10 => self.gen_ret(),
_ => self.gen_phi(ty),
}
}
fn gen_alloca(&self) -> ValueRef {
let ptr_ty = Type::pointer(0);
valref(Value::new(ptr_ty).with_subclass(SubclassKind::AllocaInst))
}
fn gen_load(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::LoadInst))
}
fn gen_add(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_sub(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_mul(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_and(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_or(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_xor(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_icmp(&self) -> ValueRef {
valref(Value::new(Type::i1()).with_subclass(SubclassKind::CmpOp))
}
fn gen_call(&self) -> ValueRef {
valref(Value::new(Type::void()).with_subclass(SubclassKind::CallInst))
}
fn gen_ret(&self) -> ValueRef {
valref(Value::new(Type::void()).with_subclass(SubclassKind::Instruction))
}
fn gen_phi(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::PHINode))
}
fn generate_random_constant(&self, ty: &Type) -> ValueRef {
match &ty.kind {
TypeKind::Integer { bits } => match bits {
1 => llvm_native_core::constants::const_bool(self.state & 1 != 0),
8 => llvm_native_core::constants::const_i8(self.state as i8),
32 => llvm_native_core::constants::const_i32(self.state as i32),
64 => llvm_native_core::constants::const_i64(self.state as i64),
_ => llvm_native_core::constants::const_int(ty.clone(), self.state as i64),
},
TypeKind::Float => llvm_native_core::constants::const_float(self.state as f32 as f64),
TypeKind::Double => llvm_native_core::constants::const_double(f64::from_bits(self.state)),
TypeKind::Pointer { .. } => llvm_native_core::constants::const_null_ptr(ty.clone()),
_ => llvm_native_core::constants::const_zero(ty.clone()),
}
}
}
impl Default for LLVMStress {
fn default() -> Self {
Self::new()
}
}
impl LLVMStress {
pub fn generate_random_type_extended(&mut self) -> Type {
let choice = self.next_rand_mod(16);
match choice {
0 => Type::i1(),
1 => Type::i8(),
2 => Type::i16(),
3 => Type::i32(),
4 => Type::i64(),
5 => Type::i128(),
6 => Type::float(),
7 => Type::double(),
8 => Type::pointer(0),
9 => {
let elem = self.generate_random_type_extended();
let num_elements = (self.next_rand_mod(8) + 2) as u32;
Type::fixed_vector_with(num_elements, elem.id)
}
10 => {
let elem = self.generate_random_type_extended();
let num_elements = (self.next_rand_mod(16) + 1) as u32;
Type::array_with(num_elements as u64, elem.id)
}
11 => {
let num_fields = (self.next_rand_mod(5) + 1) as usize;
let mut fields = Vec::with_capacity(num_fields);
for _ in 0..num_fields {
fields.push(self.generate_random_type_extended());
}
Type::struct_literal_with(false, fields.iter().map(|t| t.id).collect())
}
12 => {
Type::half()
}
13 => Type::fp128(),
14 => {
let num_fields = (self.next_rand_mod(4) + 1) as usize;
let mut fields = Vec::with_capacity(num_fields);
for _ in 0..num_fields {
fields.push(self.generate_random_type_extended());
}
Type::struct_literal_with(false, fields.iter().map(|t| t.id).collect())
}
15 => {
let pointee = self.generate_random_type_extended();
let addr_space = self.next_rand_mod(4) as u32;
Type::pointer(addr_space)
}
_ => Type::i32(),
}
}
pub fn generate_random_instruction_extended(
&mut self,
ty: &Type,
operands: &[ValueRef],
) -> ValueRef {
let choice = self.next_rand_mod(32);
match choice {
0 => self.gen_alloca(),
1 => self.gen_load(ty),
2 => self.gen_store(),
3 => self.gen_add(ty),
4 => self.gen_sub(ty),
5 => self.gen_mul(ty),
6 => self.gen_udiv(ty),
7 => self.gen_sdiv(ty),
8 => self.gen_urem(ty),
9 => self.gen_srem(ty),
10 => self.gen_and(ty),
11 => self.gen_or(ty),
12 => self.gen_xor(ty),
13 => self.gen_shl(ty),
14 => self.gen_lshr(ty),
15 => self.gen_ashr(ty),
16 => self.gen_icmp(),
17 => self.gen_fcmp(),
18 => self.gen_call(),
19 => self.gen_ret(),
20 => self.gen_phi(ty),
21 => self.gen_select(ty),
22 => self.gen_getelementptr(ty),
23 => self.gen_extractelement(ty),
24 => self.gen_insertelement(ty),
25 => self.gen_shufflevector(ty),
26 => self.gen_extractvalue(ty),
27 => self.gen_insertvalue(ty),
28 => self.gen_trunc(ty),
29 => self.gen_zext(ty),
30 => self.gen_sext(ty),
_ => self.gen_fptosi(ty),
}
}
fn gen_store(&self) -> ValueRef {
valref(Value::new(Type::void()).with_subclass(SubclassKind::StoreInst))
}
fn gen_udiv(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_sdiv(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_urem(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_srem(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_shl(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_lshr(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_ashr(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::BinaryOp))
}
fn gen_fcmp(&self) -> ValueRef {
valref(Value::new(Type::i1()).with_subclass(SubclassKind::CmpOp))
}
fn gen_select(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::Instruction))
}
fn gen_getelementptr(&self, _ty: &Type) -> ValueRef {
valref(Value::new(Type::pointer(0)).with_subclass(SubclassKind::GEPOperator))
}
fn gen_extractelement(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::Instruction))
}
fn gen_insertelement(&self, _ty: &Type) -> ValueRef {
valref(
Value::new(Type::fixed_vector_with(4, Type::i32().id))
.with_subclass(SubclassKind::Instruction),
)
}
fn gen_shufflevector(&self, _ty: &Type) -> ValueRef {
valref(
Value::new(Type::fixed_vector_with(4, Type::i32().id))
.with_subclass(SubclassKind::Instruction),
)
}
fn gen_extractvalue(&self, ty: &Type) -> ValueRef {
valref(Value::new(ty.clone()).with_subclass(SubclassKind::Instruction))
}
fn gen_insertvalue(&self, _ty: &Type) -> ValueRef {
valref(
Value::new(Type::struct_literal_with(false, vec![Type::i32().id]))
.with_subclass(SubclassKind::Instruction),
)
}
fn gen_trunc(&self, _ty: &Type) -> ValueRef {
valref(Value::new(Type::i32()).with_subclass(SubclassKind::CastOp))
}
fn gen_zext(&self, _ty: &Type) -> ValueRef {
valref(Value::new(Type::i64()).with_subclass(SubclassKind::CastOp))
}
fn gen_sext(&self, _ty: &Type) -> ValueRef {
valref(Value::new(Type::i64()).with_subclass(SubclassKind::CastOp))
}
fn gen_fptosi(&self, _ty: &Type) -> ValueRef {
valref(Value::new(Type::i32()).with_subclass(SubclassKind::CastOp))
}
pub fn generate_random_control_flow(&mut self) -> Vec<ValueRef> {
let num_blocks = (self.next_rand_mod(8) + 2) as usize;
let mut blocks = Vec::new();
for i in 0..num_blocks {
let mut bb = Value::new(Type::label());
bb.subclass = SubclassKind::BasicBlock;
bb.name = format!("bb{}", i);
blocks.push(valref(bb));
}
for i in 0..num_blocks {
let choice = self.next_rand_mod(4);
let terminator = match choice {
0 => {
let target = if i + 1 < num_blocks { i + 1 } else { 0 };
valref(Value::new(Type::void()).with_subclass(SubclassKind::Instruction))
}
1 => {
valref(Value::new(Type::void()).with_subclass(SubclassKind::Instruction))
}
2 => {
valref(Value::new(Type::void()).with_subclass(SubclassKind::Instruction))
}
_ => {
valref(Value::new(Type::void()).with_subclass(SubclassKind::Instruction))
}
};
blocks[i].borrow_mut().operands.push(terminator);
}
for i in 1..num_blocks {
if self.next_rand_mod(3) == 0 {
let phi = self.gen_phi(&Type::i32());
blocks[i].borrow_mut().operands.insert(0, phi);
}
}
blocks
}
pub fn generate_stress_module(&mut self, seed: u64) -> Module {
let module = self.generate_module(seed);
for func_ref in &module.functions {
let blocks = self.generate_random_control_flow();
let mut func = func_ref.borrow_mut();
func.operands.extend(blocks);
}
module
}
pub fn generate_random_constant_extended(&self, ty: &Type) -> ValueRef {
match &ty.kind {
TypeKind::Integer { bits } => match bits {
1 => llvm_native_core::constants::const_bool(self.state & 1 != 0),
8 => llvm_native_core::constants::const_i8(self.state as i8),
16 => llvm_native_core::constants::const_i32(self.state as i32),
32 => llvm_native_core::constants::const_i32(self.state as i32),
64 => llvm_native_core::constants::const_i64(self.state as i64),
128 => llvm_native_core::constants::const_int(ty.clone(), self.state as i64),
_ => llvm_native_core::constants::const_int(ty.clone(), self.state as i64),
},
TypeKind::Half => llvm_native_core::constants::const_float((self.state as f32) as f64),
TypeKind::Float => llvm_native_core::constants::const_float(self.state as f32 as f64),
TypeKind::Double => llvm_native_core::constants::const_double(f64::from_bits(self.state)),
TypeKind::FP128 => llvm_native_core::constants::const_double(f64::from_bits(self.state)),
TypeKind::Pointer { .. } => llvm_native_core::constants::const_null_ptr(ty.clone()),
TypeKind::FixedVector { .. } => llvm_native_core::constants::const_zero(ty.clone()),
TypeKind::Array { .. } => llvm_native_core::constants::const_zero(ty.clone()),
TypeKind::Struct { .. } => llvm_native_core::constants::const_zero(ty.clone()),
_ => llvm_native_core::constants::const_zero(ty.clone()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_module() {
let mut stress = LLVMStress::new();
let module = stress.generate_module(42);
assert!(module.name.contains("stress"));
assert_eq!(stress.modules_generated, 1);
assert!(stress.functions_generated > 0);
}
#[test]
fn test_reproducible() {
let mut stress1 = LLVMStress::new();
let m1 = stress1.generate_module(12345);
let mut stress2 = LLVMStress::new();
let m2 = stress2.generate_module(12345);
assert_eq!(m1.name, m2.name);
assert_eq!(m1.functions.len(), m2.functions.len());
}
#[test]
fn test_random_constant() {
let stress = LLVMStress::new();
let ty = Type::i32();
let c = stress.generate_random_constant(&ty);
assert!(c.borrow().subclass.is_constant());
}
#[test]
fn test_random_type() {
let mut stress = LLVMStress::new();
for _ in 0..20 {
let ty = stress.generate_random_type();
assert!(matches!(
ty.kind,
TypeKind::Integer { .. }
| TypeKind::Float
| TypeKind::Double
| TypeKind::Pointer { .. }
| TypeKind::Array { .. }
));
}
}
}