WIP.
This commit is contained in:
parent
589729566f
commit
1da150823d
|
@ -14,8 +14,8 @@ pub struct Expression(BinaryenModule, BinaryenExpression);
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Export(BinaryenModule, BinaryenExport);
|
||||
|
||||
type BinaryenIndex = u32;
|
||||
type BinaryenType = usize;
|
||||
pub type BinaryenIndex = u32;
|
||||
pub type BinaryenType = usize;
|
||||
|
||||
impl Module {
|
||||
pub fn read(data: &[u8]) -> Result<Module> {
|
||||
|
@ -423,6 +423,8 @@ pub type BinaryenModule = *const c_void;
|
|||
type BinaryenFunction = *const c_void;
|
||||
type BinaryenExpression = *const c_void;
|
||||
type BinaryenExport = *const c_void;
|
||||
type BinaryenRelooper = *const c_void;
|
||||
type BinaryenRelooperBlock = *const c_void;
|
||||
|
||||
#[repr(C)]
|
||||
struct BinaryenModuleAllocateAndWriteResult {
|
||||
|
@ -440,6 +442,74 @@ impl Drop for BinaryenModuleAllocateAndWriteResult {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Relooper(BinaryenModule, BinaryenRelooper);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RelooperBlock(BinaryenRelooperBlock);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RelooperBlockWithSwitch(BinaryenRelooperBlock);
|
||||
|
||||
impl Relooper {
|
||||
pub fn new(module: &Module) -> Relooper {
|
||||
let ptr = unsafe { RelooperCreate(module.0) };
|
||||
Relooper(module.0, ptr)
|
||||
}
|
||||
|
||||
pub fn construct(self, entry: RelooperBlock, index_var: usize) -> Expression {
|
||||
let module = self.0;
|
||||
let expr = unsafe { RelooperRenderAndDispose(self.1, entry.0, index_var as BinaryenIndex) };
|
||||
std::mem::forget(self);
|
||||
Expression(module, expr)
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self, expr: Expression) -> RelooperBlock {
|
||||
RelooperBlock(unsafe { RelooperAddBlock(self.1, expr.1) })
|
||||
}
|
||||
|
||||
pub fn add_block_with_switch(
|
||||
&mut self,
|
||||
expr: Expression,
|
||||
sel: Expression,
|
||||
) -> RelooperBlockWithSwitch {
|
||||
RelooperBlockWithSwitch(unsafe { RelooperAddBlockWithSwitch(self.1, expr.1, sel.1) })
|
||||
}
|
||||
}
|
||||
|
||||
impl RelooperBlock {
|
||||
pub fn cond_branch(&self, to: RelooperBlock, cond: Expression, edge: Expression) {
|
||||
unsafe {
|
||||
RelooperAddBranch(self.0, to.0, cond.1, edge.1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn branch(&self, to: RelooperBlock, edge: Expression) {
|
||||
unsafe {
|
||||
RelooperAddBranch(self.0, to.0, std::ptr::null(), edge.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelooperBlockWithSwitch {
|
||||
pub fn switch(&self, to: RelooperBlock, edge: Expression, indices: &[BinaryenIndex]) {
|
||||
unsafe {
|
||||
RelooperAddBranchForSwitch(
|
||||
self.0,
|
||||
to.0,
|
||||
indices.as_ptr(),
|
||||
indices.len() as BinaryenIndex,
|
||||
edge.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Relooper {
|
||||
fn drop(&mut self) {
|
||||
panic!("Relooper dropped without constructing/disposing");
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "binaryen")]
|
||||
extern "C" {
|
||||
fn BinaryenModuleRead(data: *const u8, len: usize) -> BinaryenModule;
|
||||
|
@ -613,6 +683,32 @@ extern "C" {
|
|||
fn BinaryenLiteralInt64(x: i64) -> BinaryenLiteral;
|
||||
fn BinaryenLiteralFloat32Bits(x: i32) -> BinaryenLiteral;
|
||||
fn BinaryenLiteralFloat64Bits(x: i64) -> BinaryenLiteral;
|
||||
|
||||
fn RelooperCreate(module: BinaryenModule) -> BinaryenRelooper;
|
||||
fn RelooperRenderAndDispose(
|
||||
r: BinaryenRelooper,
|
||||
entry: BinaryenRelooperBlock,
|
||||
labelVar: BinaryenIndex,
|
||||
) -> BinaryenExpression;
|
||||
fn RelooperAddBlock(r: BinaryenRelooper, code: BinaryenExpression) -> BinaryenRelooperBlock;
|
||||
fn RelooperAddBranch(
|
||||
from: BinaryenRelooperBlock,
|
||||
to: BinaryenRelooperBlock,
|
||||
cond: BinaryenExpression,
|
||||
edge_code: BinaryenExpression,
|
||||
);
|
||||
fn RelooperAddBlockWithSwitch(
|
||||
r: BinaryenRelooper,
|
||||
code: BinaryenExpression,
|
||||
selector: BinaryenExpression,
|
||||
) -> BinaryenRelooperBlock;
|
||||
fn RelooperAddBranchForSwitch(
|
||||
from: BinaryenRelooperBlock,
|
||||
to: BinaryenRelooperBlock,
|
||||
indices: *const BinaryenIndex,
|
||||
n_indices: BinaryenIndex,
|
||||
edge_code: BinaryenExpression,
|
||||
);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::backend::binaryen;
|
||||
use crate::ir::*;
|
||||
use crate::Operator;
|
||||
use fxhash::FxHashMap;
|
||||
use wasmparser::Type;
|
||||
|
||||
|
@ -10,78 +11,150 @@ pub(crate) fn generate_body(
|
|||
body: &FunctionBody,
|
||||
into_mod: &mut binaryen::Module,
|
||||
) -> (Vec<Type>, binaryen::Expression) {
|
||||
let mut ctx = ElabCtx::new(body, into_mod);
|
||||
|
||||
// For each block, generate an expr.
|
||||
let mut block_exprs: FxHashMap<BlockId, binaryen::Expression> = FxHashMap::default();
|
||||
let mut ctx = ElabCtx::new(body, into_mod);
|
||||
for block in body.blocks() {
|
||||
let exprs = body[block]
|
||||
.insts
|
||||
.iter()
|
||||
.map(|&inst| {
|
||||
.flat_map(|&inst| {
|
||||
let inst = body.resolve_alias(inst);
|
||||
elaborate_value(body, into_mod, &mut ctx, inst)
|
||||
ctx.elaborate_value(into_mod, inst)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<binaryen::Expression>>();
|
||||
block_exprs.insert(block, binaryen::Expression::block(into_mod, &exprs[..]));
|
||||
}
|
||||
|
||||
todo!()
|
||||
// Combine blocks into a single body expression, using the
|
||||
// relooper/stackifier support built into Binaryen.
|
||||
let mut relooper = binaryen::Relooper::new(into_mod);
|
||||
let mut entry = None;
|
||||
let mut relooper_blocks: FxHashMap<BlockId, binaryen::RelooperBlock> = FxHashMap::default();
|
||||
for (block_id, block_expr) in block_exprs {}
|
||||
|
||||
let index_var = ctx.new_local(Type::I32);
|
||||
let expr = relooper.construct(entry.unwrap(), index_var as usize);
|
||||
(ctx.new_locals, expr)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ElabCtx {
|
||||
value_to_expr: FxHashMap<Value, binaryen::Expression>,
|
||||
block_params: FxHashMap<(Block, usize), LocalId>,
|
||||
struct ElabCtx<'a> {
|
||||
body: &'a FunctionBody,
|
||||
op_result_locals: FxHashMap<(Value, usize), LocalId>,
|
||||
block_param_locals: FxHashMap<(BlockId, usize), LocalId>,
|
||||
new_locals: Vec<Type>,
|
||||
}
|
||||
|
||||
impl ElabCtx {
|
||||
fn new(body: &FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx {
|
||||
impl<'a> ElabCtx<'a> {
|
||||
fn new(body: &'a FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx<'a> {
|
||||
// Create locals for each blockparam.
|
||||
let mut block_params = FxHashMap::default();
|
||||
let mut next_local = body.locals.len() as LocalId;
|
||||
let mut new_locals = vec![];
|
||||
let mut this = ElabCtx {
|
||||
body,
|
||||
op_result_locals: FxHashMap::default(),
|
||||
block_param_locals: FxHashMap::default(),
|
||||
new_locals: vec![],
|
||||
};
|
||||
|
||||
for block in body.blocks() {
|
||||
for &(ty, param) in &body[block].params {
|
||||
let new_local = next_local;
|
||||
next_local += 1;
|
||||
block_params.insert((ty, param), new_local);
|
||||
new_locals.push(ty);
|
||||
for &(ty, param) in &body[block].params {}
|
||||
}
|
||||
|
||||
// Create locals for each Operator value and each blockparam.
|
||||
for (value, def) in body.values() {
|
||||
match def {
|
||||
&ValueDef::Operator(_, _, ref tys) => {
|
||||
for (i, ty) in tys.iter().copied().enumerate() {
|
||||
let local = this.new_local(ty);
|
||||
this.op_result_locals.insert((value, i), local);
|
||||
}
|
||||
}
|
||||
&ValueDef::BlockParam(block, index, ty) => {
|
||||
let local = this.new_local(ty);
|
||||
this.block_param_locals.insert((block, index), local);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ElabCtx {
|
||||
value_to_expr: FxHashMap::default(),
|
||||
block_params,
|
||||
new_locals,
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
fn elaborate_value(
|
||||
body: &FunctionBody,
|
||||
&mut self,
|
||||
into_mod: &binaryen::Module,
|
||||
ctx: &mut ElabCtx,
|
||||
value: Value,
|
||||
) -> Option<binaryen::Expression> {
|
||||
let value = self.body.resolve_alias(value);
|
||||
|
||||
match &self.body[value] {
|
||||
&ValueDef::Operator(op, ref args, ref tys) => {
|
||||
// Get expressions for each arg.
|
||||
let args = args.iter().map(|&arg| self.get_val_local(arg));
|
||||
// Create `get_local` expressions for each arg.
|
||||
let binaryen_args = args
|
||||
.map(|arg_local| into_mod.expr_local_get(arg_local, self.local_ty(arg_local)))
|
||||
.collect::<Vec<_>>();
|
||||
// Create operator.
|
||||
let expr = self.create_binaryen_op(op, binaryen_args, tys);
|
||||
|
||||
// Set local(s) as appropriate.
|
||||
if tys.len() == 0 {
|
||||
// Nothing. Create a `drop` expr that wraps the actual operator.
|
||||
Some(into_mod.expr_drop(expr))
|
||||
} else if tys.len() == 1 {
|
||||
// Set value directly.
|
||||
let local = self.get_val_local(value);
|
||||
Some(into_mod.expr_local_set(local, expr))
|
||||
} else {
|
||||
todo!("support multivalue")
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_val_local(&self, value: Value) -> LocalId {
|
||||
match &self.body[value] {
|
||||
&ValueDef::Arg(idx, _) => idx as LocalId,
|
||||
&ValueDef::BlockParam(block, idx, _) => {
|
||||
self.block_param_locals.get(&(block, idx)).copied().unwrap()
|
||||
}
|
||||
&ValueDef::Operator(..) => self.op_result_locals.get(&(value, 0)).copied().unwrap(),
|
||||
&ValueDef::PickOutput(value, idx, _) => {
|
||||
self.op_result_locals.get(&(value, idx)).copied().unwrap()
|
||||
}
|
||||
&ValueDef::Alias(val) => self.get_val_local(val),
|
||||
&ValueDef::Placeholder(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_local(&mut self, ty: Type) -> LocalId {
|
||||
let index = (self.body.locals.len() + self.new_locals.len()) as LocalId;
|
||||
self.new_locals.push(ty);
|
||||
index
|
||||
}
|
||||
|
||||
fn create_binaryen_op(
|
||||
&mut self,
|
||||
op: Operator,
|
||||
args: Vec<binaryen::Expression>,
|
||||
tys: &[Type],
|
||||
) -> binaryen::Expression {
|
||||
let value = body.resolve_alias(value);
|
||||
if let Some(expr) = ctx.value_to_expr.get(&value) {
|
||||
return *expr;
|
||||
}
|
||||
|
||||
match &body[value] {
|
||||
&ValueDef::BlockParam(block, idx) => {}
|
||||
&ValueDef::Arg(idx) => {}
|
||||
&ValueDef::PickOutput(value, idx) => {}
|
||||
&ValueDef::Operator(op, ref args) => {}
|
||||
|
||||
&ValueDef::Alias(_) => unreachable!(),
|
||||
&ValueDef::Placeholder => unreachable!(),
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn local_ty(&self, local: LocalId) -> Type {
|
||||
let index = local as usize;
|
||||
self.body
|
||||
.locals
|
||||
.get(index)
|
||||
.copied()
|
||||
.unwrap_or_else(|| self.new_locals[index - self.body.locals.len()])
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_new_func(
|
||||
module: &Module,
|
||||
sig: SignatureId,
|
||||
|
|
|
@ -128,7 +128,7 @@ fn parse_body<'a>(
|
|||
|
||||
for (arg_idx, &arg_ty) in module.signature(my_sig).params.iter().enumerate() {
|
||||
let local_idx = arg_idx as LocalId;
|
||||
let value = builder.body.add_value(ValueDef::Arg(arg_idx), vec![arg_ty]);
|
||||
let value = builder.body.add_value(ValueDef::Arg(arg_idx, arg_ty));
|
||||
trace!("defining local {} to value {}", local_idx, value);
|
||||
builder.locals.declare(local_idx, arg_ty);
|
||||
builder.locals.set(local_idx, value);
|
||||
|
@ -155,7 +155,7 @@ fn parse_body<'a>(
|
|||
assert!(builder.locals.is_sealed(block));
|
||||
}
|
||||
for value in &builder.body.values {
|
||||
assert!(value != &ValueDef::Placeholder);
|
||||
assert!(!matches!(value, &ValueDef::Placeholder(_)));
|
||||
}
|
||||
|
||||
trace!("Final function body:{:?}", ret);
|
||||
|
@ -291,32 +291,30 @@ impl LocalTracker {
|
|||
|
||||
fn create_default_value(&mut self, body: &mut FunctionBody, ty: Type) -> Value {
|
||||
match ty {
|
||||
Type::I32 => body.add_value(
|
||||
ValueDef::Operator(Operator::I32Const { value: 0 }, vec![]),
|
||||
Type::I32 => body.add_value(ValueDef::Operator(
|
||||
Operator::I32Const { value: 0 },
|
||||
vec![],
|
||||
vec![ty],
|
||||
),
|
||||
Type::I64 => body.add_value(
|
||||
ValueDef::Operator(Operator::I64Const { value: 0 }, vec![]),
|
||||
)),
|
||||
Type::I64 => body.add_value(ValueDef::Operator(
|
||||
Operator::I64Const { value: 0 },
|
||||
vec![],
|
||||
vec![ty],
|
||||
),
|
||||
Type::F32 => body.add_value(
|
||||
ValueDef::Operator(
|
||||
)),
|
||||
Type::F32 => body.add_value(ValueDef::Operator(
|
||||
Operator::F32Const {
|
||||
value: Ieee32::from_bits(0),
|
||||
},
|
||||
vec![],
|
||||
),
|
||||
vec![ty],
|
||||
),
|
||||
Type::F64 => body.add_value(
|
||||
ValueDef::Operator(
|
||||
)),
|
||||
Type::F64 => body.add_value(ValueDef::Operator(
|
||||
Operator::F64Const {
|
||||
value: Ieee64::from_bits(0),
|
||||
},
|
||||
vec![],
|
||||
),
|
||||
vec![ty],
|
||||
),
|
||||
)),
|
||||
_ => todo!("unsupported type: {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
@ -1180,7 +1178,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
|
|||
|
||||
let value = self
|
||||
.body
|
||||
.add_value(ValueDef::Operator(op, input_operands), outputs.clone());
|
||||
.add_value(ValueDef::Operator(op, input_operands, outputs.clone()));
|
||||
log::trace!(" -> value: {:?}", value);
|
||||
|
||||
if let Some(block) = self.cur_block {
|
||||
|
@ -1194,7 +1192,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
|
|||
for (i, output_ty) in outputs.into_iter().enumerate() {
|
||||
let pick = self
|
||||
.body
|
||||
.add_value(ValueDef::PickOutput(value, i), vec![output_ty]);
|
||||
.add_value(ValueDef::PickOutput(value, i, output_ty));
|
||||
self.op_stack.push((output_ty, pick));
|
||||
log::trace!(" -> pick {}: {:?} ty {:?}", i, pick, output_ty);
|
||||
}
|
||||
|
|
41
src/ir.rs
41
src/ir.rs
|
@ -110,9 +110,6 @@ pub struct FunctionBody {
|
|||
pub blocks: Vec<Block>,
|
||||
/// Value definitions, indexed by `Value`.
|
||||
pub values: Vec<ValueDef>,
|
||||
/// Types, indexed by `Value`. A single value can have multiple
|
||||
/// types if multi-value (e.g. a call).
|
||||
pub types: Vec<Vec<Type>>,
|
||||
}
|
||||
|
||||
impl FunctionBody {
|
||||
|
@ -134,12 +131,11 @@ impl FunctionBody {
|
|||
log::trace!("add_edge: from {} to {}", from, to);
|
||||
}
|
||||
|
||||
pub fn add_value(&mut self, value: ValueDef, tys: Vec<Type>) -> Value {
|
||||
log::trace!("add_value: def {:?} ty {:?}", value, tys);
|
||||
pub fn add_value(&mut self, value: ValueDef) -> Value {
|
||||
log::trace!("add_value: def {:?}", value);
|
||||
let id = Value(self.values.len() as u32);
|
||||
log::trace!(" -> value {:?}", id);
|
||||
self.values.push(value.clone());
|
||||
self.types.push(tys);
|
||||
id
|
||||
}
|
||||
|
||||
|
@ -166,30 +162,31 @@ impl FunctionBody {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn add_mutable_inst(&mut self, tys: Vec<Type>, def: ValueDef) -> Value {
|
||||
pub fn add_mutable_inst(&mut self, def: ValueDef) -> Value {
|
||||
let value = Value(self.values.len() as u32);
|
||||
self.types.push(tys);
|
||||
self.values.push(def);
|
||||
value
|
||||
}
|
||||
|
||||
pub fn add_blockparam(&mut self, block: BlockId, ty: Type) -> Value {
|
||||
let index = self.blocks[block].params.len();
|
||||
let value = self.add_value(ValueDef::BlockParam(block, index), vec![ty]);
|
||||
let value = self.add_value(ValueDef::BlockParam(block, index, ty));
|
||||
self.blocks[block].params.push((ty, value));
|
||||
value
|
||||
}
|
||||
|
||||
pub fn add_placeholder(&mut self, ty: Type) -> Value {
|
||||
self.add_mutable_inst(vec![ty], ValueDef::Placeholder)
|
||||
self.add_mutable_inst(ValueDef::Placeholder(ty))
|
||||
}
|
||||
|
||||
pub fn replace_placeholder_with_blockparam(&mut self, block: BlockId, value: Value) {
|
||||
assert!(self.values[value.index()] == ValueDef::Placeholder);
|
||||
let ty = self.types[value.index()].get(0).cloned().unwrap();
|
||||
let index = self.blocks[block].params.len();
|
||||
let ty = match &self.values[value.index()] {
|
||||
&ValueDef::Placeholder(ty) => ty,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.blocks[block].params.push((ty, value));
|
||||
self.values[value.index()] = ValueDef::BlockParam(block, index);
|
||||
self.values[value.index()] = ValueDef::BlockParam(block, index, ty);
|
||||
}
|
||||
|
||||
pub fn resolve_and_update_alias(&mut self, value: Value) -> Value {
|
||||
|
@ -295,12 +292,12 @@ impl Value {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ValueDef {
|
||||
Arg(usize),
|
||||
BlockParam(BlockId, usize),
|
||||
Operator(Operator, Vec<Value>),
|
||||
PickOutput(Value, usize),
|
||||
Arg(usize, Type),
|
||||
BlockParam(BlockId, usize, Type),
|
||||
Operator(Operator, Vec<Value>, Vec<Type>),
|
||||
PickOutput(Value, usize, Type),
|
||||
Alias(Value),
|
||||
Placeholder,
|
||||
Placeholder(Type),
|
||||
}
|
||||
|
||||
impl ValueDef {
|
||||
|
@ -308,14 +305,14 @@ impl ValueDef {
|
|||
match self {
|
||||
&ValueDef::Arg { .. } => {}
|
||||
&ValueDef::BlockParam { .. } => {}
|
||||
&ValueDef::Operator(_, ref args) => {
|
||||
&ValueDef::Operator(_, ref args, _) => {
|
||||
for &arg in args {
|
||||
f(arg);
|
||||
}
|
||||
}
|
||||
&ValueDef::PickOutput(from, ..) => f(from),
|
||||
&ValueDef::Alias(value) => f(value),
|
||||
&ValueDef::Placeholder => {}
|
||||
&ValueDef::Placeholder(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,14 +320,14 @@ impl ValueDef {
|
|||
match self {
|
||||
&mut ValueDef::Arg { .. } => {}
|
||||
&mut ValueDef::BlockParam { .. } => {}
|
||||
&mut ValueDef::Operator(_, ref mut args) => {
|
||||
&mut ValueDef::Operator(_, ref mut args, _) => {
|
||||
for arg in args {
|
||||
f(arg);
|
||||
}
|
||||
}
|
||||
&mut ValueDef::PickOutput(ref mut from, ..) => f(from),
|
||||
&mut ValueDef::Alias(ref mut value) => f(value),
|
||||
&mut ValueDef::Placeholder => {}
|
||||
&mut ValueDef::Placeholder(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ impl UseCountAnalysis {
|
|||
workqueue_set.remove(&value);
|
||||
match &f.values[value.index()] {
|
||||
&ValueDef::Alias(..) | &ValueDef::Arg(..) | &ValueDef::BlockParam(..) => {}
|
||||
&ValueDef::Operator(_op, ref args) => {
|
||||
&ValueDef::Operator(_op, ref args, _) => {
|
||||
for &arg in args {
|
||||
let arg = f.resolve_alias(arg);
|
||||
counts.add(arg);
|
||||
|
@ -52,7 +52,7 @@ impl UseCountAnalysis {
|
|||
}
|
||||
}
|
||||
}
|
||||
&ValueDef::PickOutput(value, _) => {
|
||||
&ValueDef::PickOutput(value, _, _) => {
|
||||
let value = f.resolve_alias(value);
|
||||
counts.add(value);
|
||||
if counts.use_count[value.index()] == 1 {
|
||||
|
@ -61,7 +61,7 @@ impl UseCountAnalysis {
|
|||
}
|
||||
}
|
||||
}
|
||||
&ValueDef::Placeholder => {
|
||||
&ValueDef::Placeholder(_) => {
|
||||
panic!("Unresolved placeholder for value {}", value);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue