use crate::context::{ConstantPool, DiagnosticEngine, FixItHint, LLVMContext, TypeCache};
use crate::types::{Type, TypeKind};
use crate::value::{Value, ValueRef};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::sync::{Arc, Mutex};
pub struct ThreadSafeContext {
inner: Arc<Mutex<LLVMContext>>,
}
impl ThreadSafeContext {
pub fn new() -> Self {
ThreadSafeContext {
inner: Arc::new(Mutex::new(LLVMContext::new())),
}
}
pub fn lock(&self) -> std::sync::MutexGuard<'_, LLVMContext> {
self.inner.lock().expect("LLVMContext mutex poisoned")
}
pub fn clone_handle(&self) -> Self {
ThreadSafeContext {
inner: Arc::clone(&self.inner),
}
}
}
impl Default for ThreadSafeContext {
fn default() -> Self {
ThreadSafeContext::new()
}
}
pub struct StringSaver {
strings: HashSet<String>,
storage: Vec<String>,
}
impl StringSaver {
pub fn new() -> Self {
StringSaver {
strings: HashSet::new(),
storage: Vec::new(),
}
}
pub fn save(&mut self, s: &str) -> usize {
if let Some(idx) = self.storage.iter().position(|stored| stored == s) {
return idx;
}
let idx = self.storage.len();
self.strings.insert(s.to_string());
self.storage.push(s.to_string());
idx
}
pub fn get(&self, idx: usize) -> Option<&str> {
self.storage.get(idx).map(|s| s.as_str())
}
pub fn len(&self) -> usize {
self.storage.len()
}
pub fn is_empty(&self) -> bool {
self.storage.is_empty()
}
pub fn clear(&mut self) {
self.strings.clear();
self.storage.clear();
}
}
impl Default for StringSaver {
fn default() -> Self {
StringSaver::new()
}
}
#[derive(Debug, Clone)]
pub struct IntrinsicEntry {
pub id: u32,
pub name: String,
pub return_type_hint: Option<TypeKind>,
pub param_type_hints: Vec<Option<TypeKind>>,
pub properties: IntrinsicProperties,
pub is_overloaded: bool,
}
#[derive(Debug, Clone, Default)]
pub struct IntrinsicProperties {
pub is_readnone: bool,
pub is_readonly: bool,
pub is_writeonly: bool,
pub is_argmemonly: bool,
pub is_nounwind: bool,
pub is_willreturn: bool,
pub is_nosync: bool,
pub is_nofree: bool,
pub is_nocallback: bool,
pub is_convergent: bool,
pub is_speculatable: bool,
pub is_strictfp: bool,
pub has_side_effects: bool,
pub is_commutative: bool,
pub is_noduplicate: bool,
pub is_noreturn: bool,
}
pub struct IntrinsicRegistry {
pub by_name: HashMap<String, IntrinsicEntry>,
pub by_id: HashMap<u32, IntrinsicEntry>,
next_id: u32,
}
impl IntrinsicRegistry {
pub fn new() -> Self {
let mut reg = IntrinsicRegistry {
by_name: HashMap::new(),
by_id: HashMap::new(),
next_id: 1,
};
reg.register_builtins();
reg
}
fn register_builtins(&mut self) {
self.register(
"llvm.memcpy",
false,
IntrinsicProperties {
is_argmemonly: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.memmove",
false,
IntrinsicProperties {
is_argmemonly: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.memset",
false,
IntrinsicProperties {
is_argmemonly: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.lifetime.start",
false,
IntrinsicProperties {
is_argmemonly: true,
..Default::default()
},
);
self.register(
"llvm.lifetime.end",
false,
IntrinsicProperties {
is_argmemonly: true,
..Default::default()
},
);
self.register(
"llvm.invariant.start",
false,
IntrinsicProperties {
is_readonly: true,
..Default::default()
},
);
self.register(
"llvm.invariant.end",
false,
IntrinsicProperties {
is_readonly: true,
..Default::default()
},
);
self.register(
"llvm.launder.invariant.group",
false,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
self.register(
"llvm.strip.invariant.group",
false,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
for name in &[
"llvm.sqrt",
"llvm.sin",
"llvm.cos",
"llvm.pow",
"llvm.pow.pow",
"llvm.exp",
"llvm.exp2",
"llvm.log",
"llvm.log2",
"llvm.log10",
"llvm.fma",
"llvm.fmuladd",
"llvm.fabs",
"llvm.minnum",
"llvm.maxnum",
"llvm.minimum",
"llvm.maximum",
"llvm.copysign",
"llvm.floor",
"llvm.ceil",
"llvm.trunc",
"llvm.rint",
"llvm.nearbyint",
"llvm.round",
"llvm.roundeven",
"llvm.canonicalize",
] {
self.register(
name,
true,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
}
for name in &[
"llvm.bitreverse",
"llvm.bswap",
"llvm.ctlz",
"llvm.cttz",
"llvm.ctpop",
"llvm.fshl",
"llvm.fshr",
] {
self.register(
name,
true,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
}
for name in &[
"llvm.sadd.with.overflow",
"llvm.uadd.with.overflow",
"llvm.ssub.with.overflow",
"llvm.usub.with.overflow",
"llvm.smul.with.overflow",
"llvm.umul.with.overflow",
] {
self.register(
name,
false,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
}
for name in &[
"llvm.sadd.sat",
"llvm.uadd.sat",
"llvm.ssub.sat",
"llvm.usub.sat",
"llvm.sshl.sat",
"llvm.ushl.sat",
] {
self.register(
name,
true,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
}
for name in &[
"llvm.vector.reduce.add",
"llvm.vector.reduce.mul",
"llvm.vector.reduce.and",
"llvm.vector.reduce.or",
"llvm.vector.reduce.xor",
"llvm.vector.reduce.smin",
"llvm.vector.reduce.smax",
"llvm.vector.reduce.umin",
"llvm.vector.reduce.umax",
"llvm.vector.reduce.fmin",
"llvm.vector.reduce.fmax",
"llvm.vector.reduce.fadd",
"llvm.vector.reduce.fmul",
] {
self.register(
name,
true,
IntrinsicProperties {
is_readnone: true,
..Default::default()
},
);
}
for name in &[
"llvm.matrix.multiply",
"llvm.matrix.transpose",
"llvm.matrix.column.major.load",
"llvm.matrix.column.major.store",
] {
self.register(
name,
true,
IntrinsicProperties {
is_readnone: true,
is_speculatable: true,
..Default::default()
},
);
}
self.register(
"llvm.expect",
false,
IntrinsicProperties {
is_readnone: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.expect.with.probability",
false,
IntrinsicProperties {
is_readnone: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.assume",
false,
IntrinsicProperties {
is_readnone: true,
is_willreturn: true,
..Default::default()
},
);
self.register(
"llvm.trap",
false,
IntrinsicProperties {
has_side_effects: true,
is_noreturn: true,
..Default::default()
},
);
self.register(
"llvm.debugtrap",
false,
IntrinsicProperties {
has_side_effects: true,
..Default::default()
},
);
self.register(
"llvm.ubsantrap",
false,
IntrinsicProperties {
has_side_effects: true,
is_noreturn: true,
..Default::default()
},
);
}
fn register(&mut self, name: &str, overloaded: bool, props: IntrinsicProperties) {
let id = self.next_id;
self.next_id += 1;
let entry = IntrinsicEntry {
id,
name: name.to_string(),
return_type_hint: None,
param_type_hints: Vec::new(),
properties: props,
is_overloaded: overloaded,
};
self.by_name.insert(name.to_string(), entry.clone());
self.by_id.insert(id, entry);
}
pub fn lookup(&self, name: &str) -> Option<&IntrinsicEntry> {
if let Some(entry) = self.by_name.get(name) {
return Some(entry);
}
if let Some(dot) = name.find('.') {
let base = &name[..dot];
if let Some(entry) = self.by_name.get(name) {
return Some(entry);
}
if let Some(second_dot) = name[dot + 1..].find('.') {
let prefix = &name[..dot + 1 + second_dot];
if self.by_name.contains_key(prefix) {
return self.by_name.get(prefix);
}
}
}
None
}
pub fn is_intrinsic(&self, name: &str) -> bool {
name.starts_with("llvm.") && self.lookup(name).is_some()
}
pub fn get_by_id(&self, id: u32) -> Option<&IntrinsicEntry> {
self.by_id.get(&id)
}
pub fn len(&self) -> usize {
self.by_name.len()
}
pub fn is_empty(&self) -> bool {
self.by_name.is_empty()
}
}
pub trait DiagnosticConsumer: fmt::Debug {
fn handle_diagnostic(&mut self, diag: &DiagnosticMessage);
fn finish(&mut self);
fn get_num_errors(&self) -> usize;
fn get_num_warnings(&self) -> usize;
fn clear(&mut self);
}
#[derive(Debug, Clone)]
pub struct DiagnosticMessage {
pub severity: DiagSeverity,
pub message: String,
pub file: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub ranges: Vec<SourceRange>,
pub notes: Vec<(Option<SourceLocation>, String)>,
pub fixits: Vec<FixItHint>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DiagSeverity {
Ignored = 0,
Note = 1,
Remark = 2,
Warning = 3,
Error = 4,
Fatal = 5,
}
#[derive(Debug, Clone, Copy)]
pub struct SourceRange {
pub start: SourceLocation,
pub end: SourceLocation,
}
#[derive(Debug, Clone, Copy)]
pub struct SourceLocation {
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone, Default)]
pub struct CollectingDiagnosticConsumer {
pub messages: Vec<DiagnosticMessage>,
pub num_errors: usize,
pub num_warnings: usize,
}
impl DiagnosticConsumer for CollectingDiagnosticConsumer {
fn handle_diagnostic(&mut self, diag: &DiagnosticMessage) {
match diag.severity {
DiagSeverity::Error | DiagSeverity::Fatal => self.num_errors += 1,
DiagSeverity::Warning => self.num_warnings += 1,
_ => {}
}
self.messages.push(diag.clone());
}
fn finish(&mut self) {}
fn get_num_errors(&self) -> usize {
self.num_errors
}
fn get_num_warnings(&self) -> usize {
self.num_warnings
}
fn clear(&mut self) {
self.messages.clear();
self.num_errors = 0;
self.num_warnings = 0;
}
}
pub struct SourceDiagnosticEngine {
pub consumer: Box<dyn DiagnosticConsumer>,
pub error_limit: usize,
pub warnings_as_errors: bool,
pub suppress_warnings: bool,
pub suppress_notes: bool,
pub remark_all: bool,
}
impl SourceDiagnosticEngine {
pub fn new(consumer: Box<dyn DiagnosticConsumer>) -> Self {
SourceDiagnosticEngine {
consumer,
error_limit: 20,
warnings_as_errors: false,
suppress_warnings: false,
suppress_notes: false,
remark_all: false,
}
}
pub fn emit(
&mut self,
severity: DiagSeverity,
msg: &str,
file: Option<&str>,
line: Option<u32>,
col: Option<u32>,
) {
if self.consumer.get_num_errors() >= self.error_limit && severity >= DiagSeverity::Error {
return;
}
if self.suppress_warnings && severity == DiagSeverity::Warning {
return;
}
if self.suppress_notes && severity == DiagSeverity::Note {
return;
}
let actual_severity = if self.warnings_as_errors && severity == DiagSeverity::Warning {
DiagSeverity::Error
} else {
severity
};
self.consumer.handle_diagnostic(&DiagnosticMessage {
severity: actual_severity,
message: msg.to_string(),
file: file.map(|s| s.to_string()),
line,
column: col,
ranges: Vec::new(),
notes: Vec::new(),
fixits: Vec::new(),
});
}
pub fn error(&mut self, msg: &str) {
self.emit(DiagSeverity::Error, msg, None, None, None);
}
pub fn warning(&mut self, msg: &str) {
self.emit(DiagSeverity::Warning, msg, None, None, None);
}
pub fn note(&mut self, msg: &str) {
self.emit(DiagSeverity::Note, msg, None, None, None);
}
pub fn has_errors(&self) -> bool {
self.consumer.get_num_errors() > 0
}
}
pub struct TypeCanonicalizer {
cache: HashMap<String, TypeKind>,
int_types: HashMap<u32, TypeKind>,
ptr_types: HashMap<(Box<TypeKind>, u32), TypeKind>,
array_types: HashMap<(Box<TypeKind>, u64), TypeKind>,
}
impl TypeCanonicalizer {
pub fn new() -> Self {
TypeCanonicalizer {
cache: HashMap::new(),
int_types: HashMap::new(),
ptr_types: HashMap::new(),
array_types: HashMap::new(),
}
}
pub fn get_int_type(&mut self, bits: u32) -> TypeKind {
if let Some(ty) = self.int_types.get(&bits) {
return ty.clone();
}
let ty = TypeKind::Integer { bits };
self.int_types.insert(bits, ty.clone());
ty
}
pub fn get_pointer_type(&mut self, pointee: TypeKind, addr_space: u32) -> TypeKind {
let key = (Box::new(pointee.clone()), addr_space);
if let Some(ty) = self.ptr_types.get(&key) {
return ty.clone();
}
let ty = TypeKind::Pointer { addr_space };
self.ptr_types.insert(key, ty.clone());
ty
}
pub fn get_array_type(&mut self, element_type: TypeKind, num_elements: u64) -> TypeKind {
element_type
}
pub fn get_canonical(&mut self, ty: &TypeKind) -> TypeKind {
let key = format!("{:?}", ty);
if let Some(canonical) = self.cache.get(&key) {
return canonical.clone();
}
self.cache.insert(key, ty.clone());
ty.clone()
}
pub fn clear(&mut self) {
self.cache.clear();
self.int_types.clear();
self.ptr_types.clear();
self.array_types.clear();
}
}
impl Default for TypeCanonicalizer {
fn default() -> Self {
TypeCanonicalizer::new()
}
}
#[derive(Debug, Clone)]
pub struct PassInfo {
pub pass_arg: String,
pub pass_name: String,
pub pass_type: PassType,
pub is_analysis: bool,
pub is_transform: bool,
pub is_default: bool,
pub opt_level: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PassType {
Module,
CGSCC,
Function,
Loop,
Region,
MachineFunction,
}
pub struct PassRegistry {
pub passes: HashMap<String, PassInfo>,
pub pipelines: HashMap<String, Vec<String>>,
}
impl PassRegistry {
pub fn new() -> Self {
let mut reg = PassRegistry {
passes: HashMap::new(),
pipelines: HashMap::new(),
};
reg.register_default_passes();
reg
}
fn register_default_passes(&mut self) {
let default_passes = vec![
(
"instcombine",
"Instruction Combining",
PassType::Function,
2,
),
("simplifycfg", "Simplify the CFG", PassType::Function, 2),
("gvn", "Global Value Numbering", PassType::Function, 2),
(
"sccp",
"Sparse Conditional Constant Propagation",
PassType::Function,
2,
),
("inline", "Function Inlining", PassType::CGSCC, 2),
(
"mem2reg",
"Memory to Register Promotion",
PassType::Function,
1,
),
("licm", "Loop Invariant Code Motion", PassType::Loop, 2),
("loop-unroll", "Loop Unrolling", PassType::Loop, 2),
("loop-rotate", "Loop Rotation", PassType::Loop, 1),
("loop-simplify", "Loop Simplify", PassType::Loop, 1),
(
"reassociate",
"Reassociate Expressions",
PassType::Function,
2,
),
("early-cse", "Early CSE", PassType::Function, 2),
("jump-threading", "Jump Threading", PassType::Function, 2),
(
"correlated-propagation",
"Correlated Value Propagation",
PassType::Function,
2,
),
(
"deadargelim",
"Dead Argument Elimination",
PassType::Module,
2,
),
(
"globalopt",
"Global Variable Optimization",
PassType::Module,
2,
),
("globaldce", "Dead Global Elimination", PassType::Module, 3),
("ipsccp", "Interprocedural SCCP", PassType::Module, 2),
(
"dead-store-elimination",
"Dead Store Elimination",
PassType::Function,
2,
),
(
"aggressive-instcombine",
"Aggressive InstCombine",
PassType::Function,
2,
),
(
"tailcallelim",
"Tail Call Elimination",
PassType::Function,
2,
),
("loop-vectorize", "Loop Vectorization", PassType::Loop, 3),
("slp-vectorize", "SLP Vectorizer", PassType::Function, 3),
(
"alignment-from-assumptions",
"Alignment from Assumptions",
PassType::Function,
2,
),
(
"bdce",
"Bit-Tracking Dead Code Elimination",
PassType::Function,
2,
),
("consthoist", "Constant Hoisting", PassType::Function, 2),
("div-rem-pairs", "Div Rem Pairs", PassType::Function, 2),
("float2int", "Float to Int", PassType::Function, 2),
(
"indvars",
"Induction Variable Simplification",
PassType::Loop,
2,
),
("loop-deletion", "Delete Dead Loops", PassType::Loop, 2),
("loop-idiom", "Loop Idiom Recognition", PassType::Loop, 2),
("loop-reroll", "Loop Reroll", PassType::Loop, 3),
("loop-unswitch", "Unswitch Loops", PassType::Loop, 3),
(
"loweratomic",
"Lower Atomic Intrinsics",
PassType::Function,
1,
),
("lowerinvoke", "Lower Invokes", PassType::Function, 1),
("lowerswitch", "Lower Switch", PassType::Function, 1),
("memcpyopt", "MemCpy Optimization", PassType::Function, 2),
("mergefunc", "Merge Functions", PassType::Module, 3),
(
"mergereturn",
"Unify Function Exit Nodes",
PassType::Function,
1,
),
("partial-inliner", "Partial Inliner", PassType::Module, 2),
(
"prune-eh",
"Prune Unused Exception Handling",
PassType::Function,
2,
),
(
"scalarizer",
"Scalarize Vector Instructions",
PassType::Function,
2,
),
(
"separate-const-offset-from-gep",
"Separate Const Offset from GEP",
PassType::Function,
1,
),
(
"simple-loop-unswitch",
"Simple Loop Unswitch",
PassType::Loop,
2,
),
("sink", "Code Sinking", PassType::Function, 2),
("strip", "Strip Symbols", PassType::Module, 1),
(
"strip-dead-debug-info",
"Strip Dead Debug Info",
PassType::Module,
2,
),
(
"strip-dead-prototypes",
"Strip Dead Prototypes",
PassType::Module,
2,
),
(
"strip-nondebug",
"Strip Non-Debug Symbols",
PassType::Module,
1,
),
("tailduplicate", "Tail Duplication", PassType::Function, 3),
("vector-combine", "Vector Combine", PassType::Function, 2),
("verify", "Module Verifier", PassType::Module, 1),
];
for (arg, name, pass_type, opt_level) in default_passes {
self.register(arg, name, pass_type, opt_level);
}
self.register_pipeline("O0", vec![]);
self.register_pipeline(
"O1",
vec![
"mem2reg",
"instcombine",
"simplifycfg",
"reassociate",
"gvn",
"sccp",
"deadargelim",
"licm",
],
);
self.register_pipeline(
"O2",
vec![
"inline",
"mem2reg",
"instcombine",
"simplifycfg",
"gvn",
"sccp",
"licm",
"loop-rotate",
"loop-unroll",
"reassociate",
"early-cse",
"correlated-propagation",
"tailcallelim",
"jump-threading",
"indvars",
"loop-idiom",
"loop-deletion",
],
);
self.register_pipeline(
"O3",
vec![
"inline",
"mem2reg",
"instcombine",
"simplifycfg",
"gvn",
"sccp",
"licm",
"loop-rotate",
"loop-unroll",
"loop-vectorize",
"slp-vectorize",
"reassociate",
"early-cse",
"correlated-propagation",
"tailcallelim",
"jump-threading",
"indvars",
"loop-unswitch",
"loop-idiom",
"loop-deletion",
],
);
self.register_pipeline(
"Os",
vec![
"inline",
"mem2reg",
"instcombine",
"simplifycfg",
"gvn",
"sccp",
"licm",
"loop-rotate",
],
);
self.register_pipeline(
"Oz",
vec!["inline", "mem2reg", "instcombine", "simplifycfg"],
);
}
pub fn register(&mut self, arg: &str, name: &str, pass_type: PassType, opt_level: u32) {
self.passes.insert(
arg.to_string(),
PassInfo {
pass_arg: arg.to_string(),
pass_name: name.to_string(),
pass_type,
is_analysis: false,
is_transform: true,
is_default: opt_level <= 2,
opt_level,
},
);
}
pub fn register_pipeline(&mut self, name: &str, passes: Vec<&str>) {
self.pipelines.insert(
name.to_string(),
passes.iter().map(|s| s.to_string()).collect(),
);
}
pub fn get_pipeline(&self, opt_level: &str) -> Option<&Vec<String>> {
self.pipelines.get(opt_level)
}
pub fn get_pass(&self, arg: &str) -> Option<&PassInfo> {
self.passes.get(arg)
}
pub fn all_pass_args(&self) -> Vec<&str> {
self.passes.keys().map(|s| s.as_str()).collect()
}
}
impl Default for PassRegistry {
fn default() -> Self {
PassRegistry::new()
}
}
#[derive(Debug, Clone)]
pub struct GCStrategy {
pub name: String,
pub use_statepoints: bool,
pub needs_stack_maps: bool,
pub uses_gcroot: bool,
pub custom_root_init: Option<String>,
pub custom_lowering: Option<String>,
}
impl GCStrategy {
pub fn statepoint_example() -> Self {
GCStrategy {
name: "statepoint-example".to_string(),
use_statepoints: true,
needs_stack_maps: true,
uses_gcroot: false,
custom_root_init: None,
custom_lowering: None,
}
}
pub fn coreclr() -> Self {
GCStrategy {
name: "coreclr".to_string(),
use_statepoints: true,
needs_stack_maps: true,
uses_gcroot: false,
custom_root_init: Some("coreclr_initialize_roots".to_string()),
custom_lowering: Some("coreclr_lower_gc".to_string()),
}
}
}
pub struct GCStrategyRegistry {
pub strategies: HashMap<String, GCStrategy>,
}
impl GCStrategyRegistry {
pub fn new() -> Self {
let mut reg = GCStrategyRegistry {
strategies: HashMap::new(),
};
let sp = GCStrategy::statepoint_example();
reg.strategies.insert(sp.name.clone(), sp);
let clr = GCStrategy::coreclr();
reg.strategies.insert(clr.name.clone(), clr);
reg
}
pub fn get(&self, name: &str) -> Option<&GCStrategy> {
self.strategies.get(name)
}
}
impl Default for GCStrategyRegistry {
fn default() -> Self {
GCStrategyRegistry::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_saver() {
let mut saver = StringSaver::new();
let i1 = saver.save("hello");
let i2 = saver.save("world");
let i3 = saver.save("hello");
assert_eq!(i1, i3);
assert_ne!(i1, i2);
assert_eq!(saver.len(), 2);
}
#[test]
fn test_intrinsic_registry() {
let reg = IntrinsicRegistry::new();
assert!(reg.is_intrinsic("llvm.memcpy"));
assert!(reg.is_intrinsic("llvm.sqrt"));
assert!(reg.is_intrinsic("llvm.trap"));
assert!(!reg.is_intrinsic("not_an_intrinsic"));
}
#[test]
fn test_diagnostic_consumer() {
let mut consumer = CollectingDiagnosticConsumer::default();
consumer.handle_diagnostic(&DiagnosticMessage {
severity: DiagSeverity::Error,
message: "test error".to_string(),
file: None,
line: None,
column: None,
ranges: Vec::new(),
notes: Vec::new(),
fixits: Vec::new(),
});
assert_eq!(consumer.get_num_errors(), 1);
}
#[test]
fn test_type_canonicalizer() {
let mut tc = TypeCanonicalizer::new();
let i32_a = tc.get_int_type(32);
let i32_b = tc.get_int_type(32);
assert_eq!(i32_a, i32_b);
}
#[test]
fn test_pass_registry() {
let reg = PassRegistry::new();
assert!(reg.get_pass("instcombine").is_some());
assert!(reg.get_pass("gvn").is_some());
assert!(reg.get_pipeline("O2").is_some());
}
#[test]
fn test_gc_strategy_registry() {
let reg = GCStrategyRegistry::new();
assert!(reg.get("statepoint-example").is_some());
assert!(reg.get("coreclr").is_some());
}
}