585 lines
23 KiB
Rust
585 lines
23 KiB
Rust
use crate::mir::optimizer::MirOptimizer;
|
|
use crate::mir::optimizer_stats::OptimizationStats;
|
|
use crate::mir::{
|
|
BarrierOp, EffectMask, MirModule, SpannedInstruction, TypeOpKind, ValueId, WeakRefOp,
|
|
};
|
|
use crate::ast::Span;
|
|
|
|
fn idemp_enabled() -> bool {
|
|
std::env::var("NYASH_MIR_DEV_IDEMP").ok().as_deref() == Some("1")
|
|
}
|
|
|
|
fn idemp_key(pass: &str, func_name: &str) -> String {
|
|
format!("{}:{}", pass, func_name)
|
|
}
|
|
|
|
fn idemp_already_done(module: &crate::mir::MirModule, key: &str) -> bool {
|
|
module.metadata.dev_processed_markers.contains(key)
|
|
}
|
|
|
|
fn idemp_mark(module: &mut crate::mir::MirModule, key: String) {
|
|
module.metadata.dev_processed_markers.insert(key);
|
|
}
|
|
|
|
pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats {
|
|
use crate::mir::MirInstruction as I;
|
|
let mut stats = OptimizationStats::new();
|
|
let pass_name = "normalize.force_plugin_invoke";
|
|
let func_names: Vec<String> = module.functions.keys().cloned().collect();
|
|
for fname in func_names {
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
if idemp_already_done(module, &key) {
|
|
continue;
|
|
}
|
|
}
|
|
let function = match module.functions.get_mut(&fname) {
|
|
Some(f) => f,
|
|
None => continue,
|
|
};
|
|
for (_bb, block) in &mut function.blocks {
|
|
let old_spanned = block.drain_spanned_instructions();
|
|
let mut new_spanned = Vec::with_capacity(old_spanned.len());
|
|
for SpannedInstruction { inst, span } in old_spanned {
|
|
let rewritten = match inst {
|
|
I::BoxCall {
|
|
dst,
|
|
box_val,
|
|
method,
|
|
args,
|
|
effects,
|
|
..
|
|
} => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::PluginInvoke {
|
|
dst,
|
|
box_val,
|
|
method,
|
|
args,
|
|
effects,
|
|
}
|
|
}
|
|
other => other,
|
|
};
|
|
new_spanned.push(SpannedInstruction {
|
|
inst: rewritten,
|
|
span,
|
|
});
|
|
}
|
|
|
|
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
|
|
.into_iter()
|
|
.map(|sp| (sp.inst, sp.span))
|
|
.unzip();
|
|
block.instructions = insts;
|
|
block.instruction_spans = spans;
|
|
block.effects = block
|
|
.instructions
|
|
.iter()
|
|
.chain(block.terminator.iter())
|
|
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
|
|
}
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
idemp_mark(module, key);
|
|
}
|
|
}
|
|
stats
|
|
}
|
|
|
|
pub fn normalize_python_helper_calls(
|
|
_opt: &mut MirOptimizer,
|
|
module: &mut MirModule,
|
|
) -> OptimizationStats {
|
|
use crate::mir::MirInstruction as I;
|
|
let mut stats = OptimizationStats::new();
|
|
let pass_name = "normalize.python_helper_calls";
|
|
let func_names: Vec<String> = module.functions.keys().cloned().collect();
|
|
for fname in func_names {
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
if idemp_already_done(module, &key) {
|
|
continue;
|
|
}
|
|
}
|
|
let function = match module.functions.get_mut(&fname) {
|
|
Some(f) => f,
|
|
None => continue,
|
|
};
|
|
for (_bb, block) in &mut function.blocks {
|
|
let old_spanned = block.drain_spanned_instructions();
|
|
let mut new_spanned = Vec::with_capacity(old_spanned.len());
|
|
for SpannedInstruction { mut inst, span } in old_spanned {
|
|
if let I::PluginInvoke {
|
|
ref mut box_val,
|
|
ref method,
|
|
ref mut args,
|
|
..
|
|
} = inst
|
|
{
|
|
if method == "getattr" && args.len() >= 2 {
|
|
let new_recv = args[0];
|
|
args.remove(0);
|
|
*box_val = new_recv;
|
|
stats.intrinsic_optimizations += 1;
|
|
} else if method == "call" && !args.is_empty() {
|
|
let new_recv = args[0];
|
|
args.remove(0);
|
|
*box_val = new_recv;
|
|
stats.intrinsic_optimizations += 1;
|
|
}
|
|
}
|
|
new_spanned.push(SpannedInstruction { inst, span });
|
|
}
|
|
|
|
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
|
|
.into_iter()
|
|
.map(|sp| (sp.inst, sp.span))
|
|
.unzip();
|
|
block.instructions = insts;
|
|
block.instruction_spans = spans;
|
|
block.effects = block
|
|
.instructions
|
|
.iter()
|
|
.chain(block.terminator.iter())
|
|
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
|
|
}
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
idemp_mark(module, key);
|
|
}
|
|
}
|
|
stats
|
|
}
|
|
|
|
pub fn normalize_legacy_instructions(
|
|
_opt: &mut MirOptimizer,
|
|
module: &mut MirModule,
|
|
) -> OptimizationStats {
|
|
use crate::mir::MirInstruction as I;
|
|
let mut stats = OptimizationStats::new();
|
|
let rw_dbg = crate::config::env::rewrite_debug();
|
|
let rw_sp = crate::config::env::rewrite_safepoint();
|
|
let rw_future = crate::config::env::rewrite_future();
|
|
let core13 = crate::config::env::mir_core13();
|
|
let mut array_to_boxcall = crate::config::env::mir_array_boxcall();
|
|
if core13 {
|
|
array_to_boxcall = true;
|
|
}
|
|
let pass_name = "normalize.legacy_instructions";
|
|
let func_names: Vec<String> = module.functions.keys().cloned().collect();
|
|
for fname in func_names {
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
if idemp_already_done(module, &key) {
|
|
continue;
|
|
}
|
|
}
|
|
let function = match module.functions.get_mut(&fname) {
|
|
Some(f) => f,
|
|
None => continue,
|
|
};
|
|
for (_bb, block) in &mut function.blocks {
|
|
let old_spanned = block.drain_spanned_instructions();
|
|
let mut new_spanned = Vec::with_capacity(old_spanned.len());
|
|
for SpannedInstruction { inst, span } in old_spanned {
|
|
let rewritten = match inst {
|
|
I::WeakNew { dst, box_val } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::WeakRef {
|
|
dst,
|
|
op: WeakRefOp::New,
|
|
value: box_val,
|
|
}
|
|
}
|
|
I::WeakLoad { dst, weak_ref } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::WeakRef {
|
|
dst,
|
|
op: WeakRefOp::Load,
|
|
value: weak_ref,
|
|
}
|
|
}
|
|
I::BarrierRead { ptr } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::Barrier {
|
|
op: BarrierOp::Read,
|
|
ptr,
|
|
}
|
|
}
|
|
I::BarrierWrite { ptr } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::Barrier {
|
|
op: BarrierOp::Write,
|
|
ptr,
|
|
}
|
|
}
|
|
I::Print { value, .. } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::ExternCall {
|
|
dst: None,
|
|
iface_name: "env.console".to_string(),
|
|
method_name: "log".to_string(),
|
|
args: vec![value],
|
|
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
|
|
}
|
|
}
|
|
I::ArrayGet { dst, array, index } if array_to_boxcall => {
|
|
stats.intrinsic_optimizations += 1;
|
|
let mid =
|
|
crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get");
|
|
I::BoxCall {
|
|
dst: Some(dst),
|
|
box_val: array,
|
|
method: "get".to_string(),
|
|
method_id: mid,
|
|
args: vec![index],
|
|
effects: crate::mir::EffectMask::READ,
|
|
}
|
|
}
|
|
I::ArraySet {
|
|
array,
|
|
index,
|
|
value,
|
|
} if array_to_boxcall => {
|
|
stats.intrinsic_optimizations += 1;
|
|
let mid =
|
|
crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set");
|
|
I::BoxCall {
|
|
dst: None,
|
|
box_val: array,
|
|
method: "set".to_string(),
|
|
method_id: mid,
|
|
args: vec![index, value],
|
|
effects: crate::mir::EffectMask::WRITE,
|
|
}
|
|
}
|
|
I::PluginInvoke {
|
|
dst,
|
|
box_val,
|
|
method,
|
|
args,
|
|
effects,
|
|
} => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::BoxCall {
|
|
dst,
|
|
box_val,
|
|
method,
|
|
method_id: None,
|
|
args,
|
|
effects,
|
|
}
|
|
}
|
|
I::Debug { .. } if !rw_dbg => I::Nop,
|
|
I::Safepoint if !rw_sp => I::Nop,
|
|
I::FutureNew { dst, value } if rw_future => I::ExternCall {
|
|
dst: Some(dst),
|
|
iface_name: "env.future".to_string(),
|
|
method_name: "new".to_string(),
|
|
args: vec![value],
|
|
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
|
|
},
|
|
I::FutureSet { future, value } if rw_future => I::ExternCall {
|
|
dst: None,
|
|
iface_name: "env.future".to_string(),
|
|
method_name: "set".to_string(),
|
|
args: vec![future, value],
|
|
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
|
|
},
|
|
I::Await { dst, future } if rw_future => I::ExternCall {
|
|
dst: Some(dst),
|
|
iface_name: "env.future".to_string(),
|
|
method_name: "await".to_string(),
|
|
args: vec![future],
|
|
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
|
|
},
|
|
other => other,
|
|
};
|
|
new_spanned.push(SpannedInstruction {
|
|
inst: rewritten,
|
|
span,
|
|
});
|
|
}
|
|
|
|
let terminator = block.terminator.take();
|
|
let terminator_span = block.terminator_span.take();
|
|
if let Some(term) = terminator {
|
|
let span = terminator_span.unwrap_or_else(Span::unknown);
|
|
let rewritten = match term {
|
|
I::TypeCheck {
|
|
dst,
|
|
value,
|
|
expected_type,
|
|
} => {
|
|
let ty = crate::mir::MirType::Box(expected_type);
|
|
stats.intrinsic_optimizations += 1;
|
|
I::TypeOp {
|
|
dst,
|
|
op: TypeOpKind::Check,
|
|
value,
|
|
ty,
|
|
}
|
|
}
|
|
I::Cast {
|
|
dst,
|
|
value,
|
|
target_type,
|
|
} => {
|
|
let ty = target_type;
|
|
stats.intrinsic_optimizations += 1;
|
|
I::TypeOp {
|
|
dst,
|
|
op: TypeOpKind::Cast,
|
|
value,
|
|
ty,
|
|
}
|
|
}
|
|
I::WeakNew { dst, box_val } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::WeakRef {
|
|
dst,
|
|
op: WeakRefOp::New,
|
|
value: box_val,
|
|
}
|
|
}
|
|
I::WeakLoad { dst, weak_ref } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::WeakRef {
|
|
dst,
|
|
op: WeakRefOp::Load,
|
|
value: weak_ref,
|
|
}
|
|
}
|
|
I::BarrierRead { ptr } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::Barrier {
|
|
op: BarrierOp::Read,
|
|
ptr,
|
|
}
|
|
}
|
|
I::BarrierWrite { ptr } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::Barrier {
|
|
op: BarrierOp::Write,
|
|
ptr,
|
|
}
|
|
}
|
|
I::Print { value, .. } => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::ExternCall {
|
|
dst: None,
|
|
iface_name: "env.console".to_string(),
|
|
method_name: "log".to_string(),
|
|
args: vec![value],
|
|
effects: crate::mir::EffectMask::PURE,
|
|
}
|
|
}
|
|
I::ArrayGet { dst, array, index } if array_to_boxcall => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::BoxCall {
|
|
dst: Some(dst),
|
|
box_val: array,
|
|
method: "get".to_string(),
|
|
method_id: None,
|
|
args: vec![index],
|
|
effects: crate::mir::EffectMask::READ,
|
|
}
|
|
}
|
|
I::ArraySet {
|
|
array,
|
|
index,
|
|
value,
|
|
} if array_to_boxcall => {
|
|
stats.intrinsic_optimizations += 1;
|
|
I::BoxCall {
|
|
dst: None,
|
|
box_val: array,
|
|
method: "set".to_string(),
|
|
method_id: None,
|
|
args: vec![index, value],
|
|
effects: crate::mir::EffectMask::WRITE,
|
|
}
|
|
}
|
|
other => other,
|
|
};
|
|
block.terminator = Some(rewritten);
|
|
block.terminator_span = Some(span);
|
|
}
|
|
|
|
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
|
|
.into_iter()
|
|
.map(|sp| (sp.inst, sp.span))
|
|
.unzip();
|
|
block.instructions = insts;
|
|
block.instruction_spans = spans;
|
|
block.effects = block
|
|
.instructions
|
|
.iter()
|
|
.chain(block.terminator.iter())
|
|
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
|
|
}
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
idemp_mark(module, key);
|
|
}
|
|
}
|
|
stats
|
|
}
|
|
|
|
pub fn normalize_ref_field_access(
|
|
_opt: &mut MirOptimizer,
|
|
module: &mut MirModule,
|
|
) -> OptimizationStats {
|
|
use crate::mir::MirInstruction as I;
|
|
let mut stats = OptimizationStats::new();
|
|
let pass_name = "normalize.ref_field_access";
|
|
let func_names: Vec<String> = module.functions.keys().cloned().collect();
|
|
for fname in func_names {
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
if idemp_already_done(module, &key) {
|
|
continue;
|
|
}
|
|
}
|
|
let function = match module.functions.get_mut(&fname) {
|
|
Some(f) => f,
|
|
None => continue,
|
|
};
|
|
for (_bb, block) in &mut function.blocks {
|
|
let old_spanned: Vec<SpannedInstruction> = block.drain_spanned_instructions();
|
|
let mut out: Vec<I> = Vec::with_capacity(old_spanned.len() + 2);
|
|
let mut out_spans: Vec<Span> = Vec::with_capacity(old_spanned.len() + 2);
|
|
for SpannedInstruction { inst, span } in old_spanned.into_iter() {
|
|
match inst {
|
|
I::RefGet {
|
|
dst,
|
|
reference,
|
|
field,
|
|
} => {
|
|
let new_id = ValueId::new(function.next_value_id);
|
|
function.next_value_id += 1;
|
|
out.push(I::Const {
|
|
dst: new_id,
|
|
value: crate::mir::ConstValue::String(field),
|
|
});
|
|
out_spans.push(span);
|
|
out.push(I::BoxCall {
|
|
dst: Some(dst),
|
|
box_val: reference,
|
|
method: "getField".to_string(),
|
|
method_id: None,
|
|
args: vec![new_id],
|
|
effects: crate::mir::EffectMask::READ,
|
|
});
|
|
out_spans.push(span);
|
|
stats.intrinsic_optimizations += 1;
|
|
}
|
|
I::RefSet {
|
|
reference,
|
|
field,
|
|
value,
|
|
} => {
|
|
let new_id = ValueId::new(function.next_value_id);
|
|
function.next_value_id += 1;
|
|
out.push(I::Const {
|
|
dst: new_id,
|
|
value: crate::mir::ConstValue::String(field),
|
|
});
|
|
out_spans.push(span);
|
|
out.push(I::Barrier {
|
|
op: BarrierOp::Write,
|
|
ptr: reference,
|
|
});
|
|
out_spans.push(span);
|
|
out.push(I::BoxCall {
|
|
dst: None,
|
|
box_val: reference,
|
|
method: "setField".to_string(),
|
|
method_id: None,
|
|
args: vec![new_id, value],
|
|
effects: crate::mir::EffectMask::WRITE,
|
|
});
|
|
out_spans.push(span);
|
|
stats.intrinsic_optimizations += 1;
|
|
}
|
|
other => {
|
|
out.push(other);
|
|
out_spans.push(span);
|
|
}
|
|
}
|
|
}
|
|
block.instructions = out;
|
|
block.instruction_spans = out_spans;
|
|
|
|
if let Some(term) = block.terminator.take() {
|
|
let term_span = block.terminator_span.take().unwrap_or_else(Span::unknown);
|
|
block.terminator = Some(match term {
|
|
I::RefGet {
|
|
dst,
|
|
reference,
|
|
field,
|
|
} => {
|
|
let new_id = ValueId::new(function.next_value_id);
|
|
function.next_value_id += 1;
|
|
block.instructions.push(I::Const {
|
|
dst: new_id,
|
|
value: crate::mir::ConstValue::String(field),
|
|
});
|
|
block.instruction_spans.push(term_span);
|
|
I::BoxCall {
|
|
dst: Some(dst),
|
|
box_val: reference,
|
|
method: "getField".to_string(),
|
|
method_id: None,
|
|
args: vec![new_id],
|
|
effects: crate::mir::EffectMask::READ,
|
|
}
|
|
}
|
|
I::RefSet {
|
|
reference,
|
|
field,
|
|
value,
|
|
} => {
|
|
let new_id = ValueId::new(function.next_value_id);
|
|
function.next_value_id += 1;
|
|
block.instructions.push(I::Const {
|
|
dst: new_id,
|
|
value: crate::mir::ConstValue::String(field),
|
|
});
|
|
block.instruction_spans.push(term_span);
|
|
block.instructions.push(I::Barrier {
|
|
op: BarrierOp::Write,
|
|
ptr: reference,
|
|
});
|
|
block.instruction_spans.push(term_span);
|
|
I::BoxCall {
|
|
dst: None,
|
|
box_val: reference,
|
|
method: "setField".to_string(),
|
|
method_id: None,
|
|
args: vec![new_id, value],
|
|
effects: crate::mir::EffectMask::WRITE,
|
|
}
|
|
}
|
|
other => other,
|
|
});
|
|
if block.terminator_span.is_none() {
|
|
block.terminator_span = Some(term_span);
|
|
}
|
|
}
|
|
|
|
block.effects = block
|
|
.instructions
|
|
.iter()
|
|
.chain(block.terminator.iter())
|
|
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
|
|
}
|
|
if idemp_enabled() {
|
|
let key = idemp_key(pass_name, &fname);
|
|
idemp_mark(module, key);
|
|
}
|
|
}
|
|
stats
|
|
}
|