forked from AbleOS/holey-bytes
removing useless clobbers
This commit is contained in:
parent
0c2db878f0
commit
78ebc3292c
|
@ -252,7 +252,7 @@ mod ty {
|
||||||
parser::{self, Pos},
|
parser::{self, Pos},
|
||||||
Size, Types,
|
Size, Types,
|
||||||
},
|
},
|
||||||
core::{num::NonZeroU32, ops::Range},
|
core::{num::NonZeroU32, ops::Range, usize},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ArrayLen = u32;
|
pub type ArrayLen = u32;
|
||||||
|
@ -449,6 +449,15 @@ mod ty {
|
||||||
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => unreachable!(),
|
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_pointers(&self, tys: &Types) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
Kind::Struct(s) => tys.struct_fields(s).iter().any(|f| f.ty.has_pointers(tys)),
|
||||||
|
Kind::Ptr(_) => true,
|
||||||
|
Kind::Slice(s) => tys.ins.slices[s as usize].len == ArrayLen::MAX,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
|
|
@ -16,7 +16,7 @@ use {
|
||||||
Comptime, Func, Global, HashMap, Offset, OffsetIter, PLoc, Reloc, Sig, SymKey, TypeParser,
|
Comptime, Func, Global, HashMap, Offset, OffsetIter, PLoc, Reloc, Sig, SymKey, TypeParser,
|
||||||
TypedReloc, Types,
|
TypedReloc, Types,
|
||||||
},
|
},
|
||||||
alloc::{string::String, vec::Vec},
|
alloc::{borrow::ToOwned, string::String, vec::Vec},
|
||||||
core::{
|
core::{
|
||||||
assert_matches::debug_assert_matches,
|
assert_matches::debug_assert_matches,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
@ -27,7 +27,6 @@ use {
|
||||||
hashbrown::hash_map,
|
hashbrown::hash_map,
|
||||||
hbbytecode::DisasmError,
|
hbbytecode::DisasmError,
|
||||||
regalloc2::VReg,
|
regalloc2::VReg,
|
||||||
std::borrow::ToOwned,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VOID: Nid = 0;
|
const VOID: Nid = 0;
|
||||||
|
@ -45,7 +44,7 @@ trait StoreId: Sized {
|
||||||
|
|
||||||
impl StoreId for Nid {
|
impl StoreId for Nid {
|
||||||
fn to_store(self) -> Option<Self> {
|
fn to_store(self) -> Option<Self> {
|
||||||
(self != ENTRY).then_some(self)
|
(self != MEM).then_some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1134,7 +1133,7 @@ mod var {
|
||||||
use {
|
use {
|
||||||
super::{Kind, Nid, Nodes},
|
super::{Kind, Nid, Nodes},
|
||||||
crate::{ident::Ident, ty},
|
crate::{ident::Ident, ty},
|
||||||
std::{panic, vec::Vec},
|
alloc::vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
// makes sure value inside is laways locked for this instance of variable
|
// makes sure value inside is laways locked for this instance of variable
|
||||||
|
@ -1158,7 +1157,7 @@ mod var {
|
||||||
|
|
||||||
pub fn set_value(&mut self, mut new_value: Nid, nodes: &mut Nodes) -> Nid {
|
pub fn set_value(&mut self, mut new_value: Nid, nodes: &mut Nodes) -> Nid {
|
||||||
nodes.unlock(self.value);
|
nodes.unlock(self.value);
|
||||||
std::mem::swap(&mut self.value, &mut new_value);
|
core::mem::swap(&mut self.value, &mut new_value);
|
||||||
nodes.lock(self.value);
|
nodes.lock(self.value);
|
||||||
new_value
|
new_value
|
||||||
}
|
}
|
||||||
|
@ -1170,7 +1169,7 @@ mod var {
|
||||||
|
|
||||||
pub fn remove(self, nodes: &mut Nodes) {
|
pub fn remove(self, nodes: &mut Nodes) {
|
||||||
nodes.unlock_remove(self.value);
|
nodes.unlock_remove(self.value);
|
||||||
std::mem::forget(self);
|
core::mem::forget(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_value_remove(&mut self, new_value: Nid, nodes: &mut Nodes) {
|
pub fn set_value_remove(&mut self, new_value: Nid, nodes: &mut Nodes) {
|
||||||
|
@ -1184,7 +1183,7 @@ mod var {
|
||||||
} else {
|
} else {
|
||||||
nodes.unlock_remove(self.value);
|
nodes.unlock_remove(self.value);
|
||||||
}
|
}
|
||||||
std::mem::forget(self);
|
core::mem::forget(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1219,7 +1218,7 @@ mod var {
|
||||||
pub fn clear(&mut self, nodes: &mut Nodes) {
|
pub fn clear(&mut self, nodes: &mut Nodes) {
|
||||||
self.vars.drain(..).for_each(|n| n.remove(nodes));
|
self.vars.drain(..).for_each(|n| n.remove(nodes));
|
||||||
self.loads.drain(..).for_each(|l| _ = nodes.unlock_remove(l));
|
self.loads.drain(..).for_each(|l| _ = nodes.unlock_remove(l));
|
||||||
std::mem::take(&mut self.store).remove(nodes);
|
core::mem::take(&mut self.store).remove(nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1268,15 +1267,17 @@ impl ItemCtx {
|
||||||
self.nodes.lock(end);
|
self.nodes.lock(end);
|
||||||
self.ctrl = self.nodes.new_node(ty::Id::VOID, Kind::Entry, [VOID]);
|
self.ctrl = self.nodes.new_node(ty::Id::VOID, Kind::Entry, [VOID]);
|
||||||
debug_assert_eq!(self.ctrl, ENTRY);
|
debug_assert_eq!(self.ctrl, ENTRY);
|
||||||
|
self.nodes.lock(self.ctrl);
|
||||||
let mem = self.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]);
|
let mem = self.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]);
|
||||||
debug_assert_eq!(mem, MEM);
|
debug_assert_eq!(mem, MEM);
|
||||||
self.nodes.lock(mem);
|
self.nodes.lock(mem);
|
||||||
self.scope.store = Variable::new(0, ty::Id::VOID, false, ENTRY, &mut self.nodes);
|
self.scope.store = Variable::new(0, ty::Id::VOID, false, MEM, &mut self.nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(&mut self) {
|
fn finalize(&mut self) {
|
||||||
self.scope.clear(&mut self.nodes);
|
self.scope.clear(&mut self.nodes);
|
||||||
self.nodes.unlock(NEVER);
|
self.nodes.unlock(NEVER);
|
||||||
|
self.nodes.unlock(ENTRY);
|
||||||
self.nodes.unlock(MEM);
|
self.nodes.unlock(MEM);
|
||||||
self.nodes.eliminate_stack_temporaries();
|
self.nodes.eliminate_stack_temporaries();
|
||||||
}
|
}
|
||||||
|
@ -1609,7 +1610,7 @@ impl ItemCtx {
|
||||||
self.nodes.graphviz(tys, files);
|
self.nodes.graphviz(tys, files);
|
||||||
|
|
||||||
debug_assert!(self.code.is_empty());
|
debug_assert!(self.code.is_empty());
|
||||||
let tail = std::mem::take(&mut self.call_count) == 0;
|
let tail = core::mem::take(&mut self.call_count) == 0;
|
||||||
|
|
||||||
'_open_function: {
|
'_open_function: {
|
||||||
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
|
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
|
||||||
|
@ -1620,10 +1621,20 @@ impl ItemCtx {
|
||||||
'_compute_stack: {
|
'_compute_stack: {
|
||||||
let mems = core::mem::take(&mut self.nodes[MEM].outputs);
|
let mems = core::mem::take(&mut self.nodes[MEM].outputs);
|
||||||
for &stck in mems.iter() {
|
for &stck in mems.iter() {
|
||||||
|
if !matches!(self.nodes[stck].kind, Kind::Stck | Kind::Arg) {
|
||||||
|
debug_assert_matches!(
|
||||||
|
self.nodes[stck].kind,
|
||||||
|
Kind::Phi | Kind::Return | Kind::Load
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
stack_size += tys.size_of(self.nodes[stck].ty);
|
stack_size += tys.size_of(self.nodes[stck].ty);
|
||||||
self.nodes[stck].offset = stack_size;
|
self.nodes[stck].offset = stack_size;
|
||||||
}
|
}
|
||||||
for &stck in mems.iter() {
|
for &stck in mems.iter() {
|
||||||
|
if !matches!(self.nodes[stck].kind, Kind::Stck | Kind::Arg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
self.nodes[stck].offset = stack_size - self.nodes[stck].offset;
|
self.nodes[stck].offset = stack_size - self.nodes[stck].offset;
|
||||||
}
|
}
|
||||||
self.nodes[MEM].outputs = mems;
|
self.nodes[MEM].outputs = mems;
|
||||||
|
@ -1837,7 +1848,7 @@ impl TypeParser for Codegen<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_const(&mut self, file: FileId, expr: &Expr, ret: ty::Id) -> u64 {
|
fn eval_const(&mut self, file: FileId, expr: &Expr, ret: ty::Id) -> u64 {
|
||||||
let mut scope = std::mem::take(&mut self.ci.scope.vars);
|
let mut scope = core::mem::take(&mut self.ci.scope.vars);
|
||||||
self.pool.push_ci(file, Some(ret), self.tasks.len(), &mut self.ci);
|
self.pool.push_ci(file, Some(ret), self.tasks.len(), &mut self.ci);
|
||||||
self.ci.scope.vars = scope;
|
self.ci.scope.vars = scope;
|
||||||
|
|
||||||
|
@ -1845,7 +1856,7 @@ impl TypeParser for Codegen<'_> {
|
||||||
|
|
||||||
self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) });
|
self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) });
|
||||||
|
|
||||||
scope = std::mem::take(&mut self.ci.scope.vars);
|
scope = core::mem::take(&mut self.ci.scope.vars);
|
||||||
self.ci.finalize();
|
self.ci.finalize();
|
||||||
|
|
||||||
if self.errors.borrow().len() == prev_err_len {
|
if self.errors.borrow().len() == prev_err_len {
|
||||||
|
@ -2187,6 +2198,7 @@ impl<'a> Codegen<'a> {
|
||||||
self.assert_ty(pos, &mut value, expected, "return value");
|
self.assert_ty(pos, &mut value, expected, "return value");
|
||||||
|
|
||||||
if self.ci.inline_depth == 0 {
|
if self.ci.inline_depth == 0 {
|
||||||
|
debug_assert_ne!(self.ci.ctrl, VOID);
|
||||||
let mut inps = Vc::from([self.ci.ctrl, value.id]);
|
let mut inps = Vc::from([self.ci.ctrl, value.id]);
|
||||||
self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
|
self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
|
||||||
if let Some(str) = self.ci.scope.store.value().to_store() {
|
if let Some(str) = self.ci.scope.store.value().to_store() {
|
||||||
|
@ -2610,10 +2622,12 @@ impl<'a> Codegen<'a> {
|
||||||
let mut tys = sig.args.args();
|
let mut tys = sig.args.args();
|
||||||
let mut cargs = cargs.iter();
|
let mut cargs = cargs.iter();
|
||||||
let mut args = args.iter();
|
let mut args = args.iter();
|
||||||
|
let mut has_ptr_arg = false;
|
||||||
while let Some(ty) = tys.next(&self.tys) {
|
while let Some(ty) = tys.next(&self.tys) {
|
||||||
let carg = cargs.next().unwrap();
|
let carg = cargs.next().unwrap();
|
||||||
let arg = args.next().unwrap();
|
let arg = args.next().unwrap();
|
||||||
let Arg::Value(ty) = ty else { continue };
|
let Arg::Value(ty) = ty else { continue };
|
||||||
|
has_ptr_arg |= ty.has_pointers(&self.tys);
|
||||||
|
|
||||||
let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
|
let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
|
||||||
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
|
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
|
||||||
|
@ -2627,20 +2641,22 @@ impl<'a> Codegen<'a> {
|
||||||
self.ci.nodes.unlock(n);
|
self.ci.nodes.unlock(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(str) = self.ci.scope.store.value().to_store() {
|
if has_ptr_arg {
|
||||||
inps.push(str);
|
if let Some(str) = self.ci.scope.store.value().to_store() {
|
||||||
|
inps.push(str);
|
||||||
|
}
|
||||||
|
self.ci.scope.loads.retain(|&load| {
|
||||||
|
if inps.contains(&load) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.ci.nodes.unlock_remove(load) {
|
||||||
|
inps.push(load);
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self.ci.scope.loads.retain(|&load| {
|
|
||||||
if inps.contains(&load) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.ci.nodes.unlock_remove(load) {
|
|
||||||
inps.push(load);
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
let alt_value = match sig.ret.loc(&self.tys) {
|
let alt_value = match sig.ret.loc(&self.tys) {
|
||||||
Loc::Reg => None,
|
Loc::Reg => None,
|
||||||
|
@ -2655,7 +2671,9 @@ impl<'a> Codegen<'a> {
|
||||||
self.ci.ctrl =
|
self.ci.ctrl =
|
||||||
self.ci.nodes.new_node(sig.ret, Kind::Call { func: fu, args: sig.args }, inps);
|
self.ci.nodes.new_node(sig.ret, Kind::Call { func: fu, args: sig.args }, inps);
|
||||||
|
|
||||||
self.store_mem(VOID, ty::Id::VOID, VOID);
|
if has_ptr_arg {
|
||||||
|
self.store_mem(VOID, ty::Id::VOID, VOID);
|
||||||
|
}
|
||||||
|
|
||||||
alt_value.or(Some(Value::new(self.ci.ctrl).ty(sig.ret)))
|
alt_value.or(Some(Value::new(self.ci.ctrl).ty(sig.ret)))
|
||||||
}
|
}
|
||||||
|
@ -2737,7 +2755,7 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev_var_base =
|
let prev_var_base =
|
||||||
std::mem::replace(&mut self.ci.inline_var_base, self.ci.scope.vars.len());
|
core::mem::replace(&mut self.ci.inline_var_base, self.ci.scope.vars.len());
|
||||||
let prev_ret = self.ci.ret.replace(sig.ret);
|
let prev_ret = self.ci.ret.replace(sig.ret);
|
||||||
let prev_inline_ret = self.ci.inline_ret.take();
|
let prev_inline_ret = self.ci.inline_ret.take();
|
||||||
let prev_file = core::mem::replace(&mut self.ci.file, file);
|
let prev_file = core::mem::replace(&mut self.ci.file, file);
|
||||||
|
@ -3950,7 +3968,6 @@ impl<'a> Function<'a> {
|
||||||
}
|
}
|
||||||
Kind::Stck
|
Kind::Stck
|
||||||
if node.ty.loc(self.tys) == Loc::Reg && node.outputs.iter().all(|&n| {
|
if node.ty.loc(self.tys) == Loc::Reg && node.outputs.iter().all(|&n| {
|
||||||
|
|
||||||
matches!(self.nodes[n].kind, Kind::Stre | Kind::Load)
|
matches!(self.nodes[n].kind, Kind::Stre | Kind::Load)
|
||||||
|| matches!(self.nodes[n].kind, Kind::BinOp { op: TokenKind::Add }
|
|| matches!(self.nodes[n].kind, Kind::BinOp { op: TokenKind::Add }
|
||||||
if self.nodes.is_const(self.nodes[n].inputs[2])
|
if self.nodes.is_const(self.nodes[n].inputs[2])
|
||||||
|
@ -4425,7 +4442,7 @@ mod tests {
|
||||||
different_types;
|
different_types;
|
||||||
struct_return_from_module_function;
|
struct_return_from_module_function;
|
||||||
sort_something_viredly;
|
sort_something_viredly;
|
||||||
//structs_in_registers;
|
structs_in_registers;
|
||||||
comptime_function_from_another_file;
|
comptime_function_from_another_file;
|
||||||
inline_test;
|
inline_test;
|
||||||
inlined_generic_functions;
|
inlined_generic_functions;
|
||||||
|
|
|
@ -23,11 +23,11 @@ main:
|
||||||
JAL r31, r0, :multiple_breaks
|
JAL r31, r0, :multiple_breaks
|
||||||
CP r3, r1
|
CP r3, r1
|
||||||
LI64 r1, 3d
|
LI64 r1, 3d
|
||||||
LI64 r6, 1d
|
LI64 r7, 1d
|
||||||
JEQ r3, r1, :0
|
JEQ r3, r1, :0
|
||||||
CP r1, r6
|
CP r1, r7
|
||||||
JMP :1
|
JMP :1
|
||||||
0: CP r33, r6
|
0: CP r33, r7
|
||||||
CP r34, r1
|
CP r34, r1
|
||||||
LI64 r35, 4d
|
LI64 r35, 4d
|
||||||
CP r2, r35
|
CP r2, r35
|
||||||
|
|
|
@ -10,28 +10,28 @@ foo:
|
||||||
ADDI64 r254, r254, 16d
|
ADDI64 r254, r254, 16d
|
||||||
JALA r0, r31, 0a
|
JALA r0, r31, 0a
|
||||||
main:
|
main:
|
||||||
ADDI64 r254, r254, -72d
|
ADDI64 r254, r254, -56d
|
||||||
ST r31, r254, 48a, 24h
|
ST r31, r254, 48a, 8h
|
||||||
ADDI64 r2, r254, 32d
|
ADDI64 r2, r254, 32d
|
||||||
JAL r31, r0, :foo
|
JAL r31, r0, :foo
|
||||||
ST r1, r254, 32a, 16h
|
ST r1, r254, 32a, 16h
|
||||||
ADDI64 r8, r254, 16d
|
ADDI64 r8, r254, 16d
|
||||||
LD r32, r254, 32a, 8h
|
|
||||||
JAL r31, r0, :foo
|
JAL r31, r0, :foo
|
||||||
ST r1, r254, 16a, 16h
|
ST r1, r254, 16a, 16h
|
||||||
ADDI64 r3, r254, 0d
|
ADDI64 r2, r254, 0d
|
||||||
LD r33, r254, 24a, 4h
|
|
||||||
JAL r31, r0, :foo
|
JAL r31, r0, :foo
|
||||||
ST r1, r254, 0a, 16h
|
ST r1, r254, 0a, 16h
|
||||||
LI64 r9, 7d
|
LI64 r7, 7d
|
||||||
LD r10, r254, 12a, 4h
|
LD r8, r254, 12a, 4h
|
||||||
ANDI r11, r10, 4294967295d
|
ANDI r9, r8, 4294967295d
|
||||||
ANDI r8, r33, 4294967295d
|
LD r5, r254, 24a, 4h
|
||||||
ADD64 r12, r32, r8
|
ANDI r8, r5, 4294967295d
|
||||||
ADD64 r3, r12, r11
|
LD r3, r254, 32a, 8h
|
||||||
SUB64 r1, r9, r3
|
ADD64 r11, r3, r8
|
||||||
LD r31, r254, 48a, 24h
|
ADD64 r3, r11, r9
|
||||||
ADDI64 r254, r254, 72d
|
SUB64 r1, r7, r3
|
||||||
|
LD r31, r254, 48a, 8h
|
||||||
|
ADDI64 r254, r254, 56d
|
||||||
JALA r0, r31, 0a
|
JALA r0, r31, 0a
|
||||||
code size: 359
|
code size: 359
|
||||||
ret: 0
|
ret: 0
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
main:
|
main:
|
||||||
ADDI64 r254, r254, -88d
|
ADDI64 r254, r254, -96d
|
||||||
ST r31, r254, 64a, 24h
|
ST r31, r254, 64a, 32h
|
||||||
LI64 r32, 3d
|
LI64 r32, 3d
|
||||||
LI64 r3, 4d
|
LI64 r3, 4d
|
||||||
ADDI64 r33, r254, 0d
|
ADDI64 r33, r254, 0d
|
||||||
ADDI64 r7, r254, 16d
|
ADDI64 r34, r254, 48d
|
||||||
ADDI64 r5, r254, 48d
|
|
||||||
ST r3, r254, 48a, 8h
|
ST r3, r254, 48a, 8h
|
||||||
ST r32, r254, 56a, 8h
|
ST r32, r254, 56a, 8h
|
||||||
BMC r5, r7, 16h
|
LD r3, r34, 0a, 16h
|
||||||
LD r3, r5, 0a, 16h
|
|
||||||
JAL r31, r0, :odher_pass
|
JAL r31, r0, :odher_pass
|
||||||
ST r1, r254, 0a, 16h
|
ST r1, r254, 0a, 16h
|
||||||
|
ADDI64 r11, r254, 16d
|
||||||
ADDI64 r2, r254, 32d
|
ADDI64 r2, r254, 32d
|
||||||
|
BMC r34, r11, 16h
|
||||||
BMC r33, r2, 16h
|
BMC r33, r2, 16h
|
||||||
LD r7, r254, 40a, 8h
|
LD r7, r254, 40a, 8h
|
||||||
JNE r7, r32, :0
|
JNE r7, r32, :0
|
||||||
JAL r31, r0, :pass
|
JAL r31, r0, :pass
|
||||||
JMP :1
|
JMP :1
|
||||||
0: LI64 r1, 0d
|
0: LI64 r1, 0d
|
||||||
1: LD r31, r254, 64a, 24h
|
1: LD r31, r254, 64a, 32h
|
||||||
ADDI64 r254, r254, 88d
|
ADDI64 r254, r254, 96d
|
||||||
JALA r0, r31, 0a
|
JALA r0, r31, 0a
|
||||||
odher_pass:
|
odher_pass:
|
||||||
ADDI64 r254, r254, -16d
|
ADDI64 r254, r254, -16d
|
||||||
|
|
Loading…
Reference in a new issue