mirror of
https://github.com/corda/corda.git
synced 2025-01-22 12:28:11 +00:00
move BranchEvent out of compiler.cpp
This commit is contained in:
parent
18e6f28ff4
commit
65b7cf047c
@ -1586,243 +1586,6 @@ register_(Context* c, int number)
|
|||||||
return value(c, type, s, s);
|
return value(c, type, s, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
|
||||||
asFloat(unsigned size, int64_t v)
|
|
||||||
{
|
|
||||||
if (size == 4) {
|
|
||||||
return bitsToFloat(v);
|
|
||||||
} else {
|
|
||||||
return bitsToDouble(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
unordered(double a, double b)
|
|
||||||
{
|
|
||||||
return not (a >= b or a < b);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
shouldJump(Context* c, lir::TernaryOperation type, unsigned size, int64_t b,
|
|
||||||
int64_t a)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case lir::JumpIfEqual:
|
|
||||||
return a == b;
|
|
||||||
|
|
||||||
case lir::JumpIfNotEqual:
|
|
||||||
return a != b;
|
|
||||||
|
|
||||||
case lir::JumpIfLess:
|
|
||||||
return a < b;
|
|
||||||
|
|
||||||
case lir::JumpIfGreater:
|
|
||||||
return a > b;
|
|
||||||
|
|
||||||
case lir::JumpIfLessOrEqual:
|
|
||||||
return a <= b;
|
|
||||||
|
|
||||||
case lir::JumpIfGreaterOrEqual:
|
|
||||||
return a >= b;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatEqual:
|
|
||||||
return asFloat(size, a) == asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatNotEqual:
|
|
||||||
return asFloat(size, a) != asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLess:
|
|
||||||
return asFloat(size, a) < asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreater:
|
|
||||||
return asFloat(size, a) > asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLessOrEqual:
|
|
||||||
return asFloat(size, a) <= asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreaterOrEqual:
|
|
||||||
return asFloat(size, a) >= asFloat(size, b);
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLessOrUnordered:
|
|
||||||
return asFloat(size, a) < asFloat(size, b)
|
|
||||||
or unordered(asFloat(size, a), asFloat(size, b));
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreaterOrUnordered:
|
|
||||||
return asFloat(size, a) > asFloat(size, b)
|
|
||||||
or unordered(asFloat(size, a), asFloat(size, b));
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLessOrEqualOrUnordered:
|
|
||||||
return asFloat(size, a) <= asFloat(size, b)
|
|
||||||
or unordered(asFloat(size, a), asFloat(size, b));
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreaterOrEqualOrUnordered:
|
|
||||||
return asFloat(size, a) >= asFloat(size, b)
|
|
||||||
or unordered(asFloat(size, a), asFloat(size, b));
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lir::TernaryOperation
|
|
||||||
thunkBranch(Context* c, lir::TernaryOperation type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case lir::JumpIfFloatEqual:
|
|
||||||
return lir::JumpIfEqual;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatNotEqual:
|
|
||||||
return lir::JumpIfNotEqual;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLess:
|
|
||||||
case lir::JumpIfFloatLessOrUnordered:
|
|
||||||
return lir::JumpIfLess;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreater:
|
|
||||||
case lir::JumpIfFloatGreaterOrUnordered:
|
|
||||||
return lir::JumpIfGreater;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatLessOrEqual:
|
|
||||||
case lir::JumpIfFloatLessOrEqualOrUnordered:
|
|
||||||
return lir::JumpIfLessOrEqual;
|
|
||||||
|
|
||||||
case lir::JumpIfFloatGreaterOrEqual:
|
|
||||||
case lir::JumpIfFloatGreaterOrEqualOrUnordered:
|
|
||||||
return lir::JumpIfGreaterOrEqual;
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BranchEvent: public Event {
|
|
||||||
public:
|
|
||||||
BranchEvent(Context* c, lir::TernaryOperation type, unsigned size,
|
|
||||||
Value* first, Value* second, Value* address,
|
|
||||||
const SiteMask& firstLowMask,
|
|
||||||
const SiteMask& firstHighMask,
|
|
||||||
const SiteMask& secondLowMask,
|
|
||||||
const SiteMask& secondHighMask):
|
|
||||||
Event(c), type(type), size(size), first(first), second(second),
|
|
||||||
address(address)
|
|
||||||
{
|
|
||||||
this->addReads(c, first, size, firstLowMask, firstHighMask);
|
|
||||||
this->addReads(c, second, size, secondLowMask, secondHighMask);
|
|
||||||
|
|
||||||
uint8_t typeMask;
|
|
||||||
uint64_t registerMask;
|
|
||||||
c->arch->planDestination(type, size, 0, 0, size, 0, 0, TargetBytesPerWord,
|
|
||||||
&typeMask, ®isterMask);
|
|
||||||
|
|
||||||
this->addRead(c, address, SiteMask(typeMask, registerMask, AnyFrameIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* name() {
|
|
||||||
return "BranchEvent";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void compile(Context* c) {
|
|
||||||
ConstantSite* firstConstant = findConstantSite(c, first);
|
|
||||||
ConstantSite* secondConstant = findConstantSite(c, second);
|
|
||||||
|
|
||||||
if (not this->isUnreachable()) {
|
|
||||||
if (firstConstant
|
|
||||||
and secondConstant
|
|
||||||
and firstConstant->value->resolved()
|
|
||||||
and secondConstant->value->resolved())
|
|
||||||
{
|
|
||||||
int64_t firstValue = firstConstant->value->value();
|
|
||||||
int64_t secondValue = secondConstant->value->value();
|
|
||||||
|
|
||||||
if (size > TargetBytesPerWord) {
|
|
||||||
firstValue |= findConstantSite
|
|
||||||
(c, first->nextWord)->value->value() << 32;
|
|
||||||
secondValue |= findConstantSite
|
|
||||||
(c, second->nextWord)->value->value() << 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldJump(c, type, size, firstValue, secondValue)) {
|
|
||||||
apply(c, lir::Jump, TargetBytesPerWord, address->source, address->source);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
freezeSource(c, size, first);
|
|
||||||
freezeSource(c, size, second);
|
|
||||||
freezeSource(c, TargetBytesPerWord, address);
|
|
||||||
|
|
||||||
apply(c, type, size, first->source, first->nextWord->source,
|
|
||||||
size, second->source, second->nextWord->source,
|
|
||||||
TargetBytesPerWord, address->source, address->source);
|
|
||||||
|
|
||||||
thawSource(c, TargetBytesPerWord, address);
|
|
||||||
thawSource(c, size, second);
|
|
||||||
thawSource(c, size, first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Read* r = reads; r; r = r->eventNext) {
|
|
||||||
popRead(c, this, r->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool isBranch() { return true; }
|
|
||||||
|
|
||||||
lir::TernaryOperation type;
|
|
||||||
unsigned size;
|
|
||||||
Value* first;
|
|
||||||
Value* second;
|
|
||||||
Value* address;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
appendBranch(Context* c, lir::TernaryOperation type, unsigned size, Value* first,
|
|
||||||
Value* second, Value* address)
|
|
||||||
{
|
|
||||||
bool thunk;
|
|
||||||
uint8_t firstTypeMask;
|
|
||||||
uint64_t firstRegisterMask;
|
|
||||||
uint8_t secondTypeMask;
|
|
||||||
uint64_t secondRegisterMask;
|
|
||||||
|
|
||||||
c->arch->planSource(type, size, &firstTypeMask, &firstRegisterMask,
|
|
||||||
size, &secondTypeMask, &secondRegisterMask,
|
|
||||||
TargetBytesPerWord, &thunk);
|
|
||||||
|
|
||||||
if (thunk) {
|
|
||||||
Stack* oldStack = c->stack;
|
|
||||||
|
|
||||||
bool threadParameter;
|
|
||||||
intptr_t handler = c->client->getThunk
|
|
||||||
(type, size, size, &threadParameter);
|
|
||||||
|
|
||||||
assert(c, not threadParameter);
|
|
||||||
|
|
||||||
compiler::push(c, ceilingDivide(size, TargetBytesPerWord), second);
|
|
||||||
compiler::push(c, ceilingDivide(size, TargetBytesPerWord), first);
|
|
||||||
|
|
||||||
Stack* argumentStack = c->stack;
|
|
||||||
c->stack = oldStack;
|
|
||||||
|
|
||||||
Value* result = value(c, lir::ValueGeneral);
|
|
||||||
appendCall
|
|
||||||
(c, value
|
|
||||||
(c, lir::ValueGeneral, constantSite(c, handler)), 0, 0, result, 4,
|
|
||||||
argumentStack, ceilingDivide(size, TargetBytesPerWord) * 2, 0);
|
|
||||||
|
|
||||||
appendBranch(c, thunkBranch(c, type), 4, value
|
|
||||||
(c, lir::ValueGeneral, constantSite(c, static_cast<int64_t>(0))),
|
|
||||||
result, address);
|
|
||||||
} else {
|
|
||||||
append
|
|
||||||
(c, new(c->zone)
|
|
||||||
BranchEvent
|
|
||||||
(c, type, size, first, second, address,
|
|
||||||
SiteMask(firstTypeMask, firstRegisterMask, AnyFrameIndex),
|
|
||||||
SiteMask(firstTypeMask, firstRegisterMask >> 32, AnyFrameIndex),
|
|
||||||
SiteMask(secondTypeMask, secondRegisterMask, AnyFrameIndex),
|
|
||||||
SiteMask(secondTypeMask, secondRegisterMask >> 32, AnyFrameIndex)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JumpEvent: public Event {
|
class JumpEvent: public Event {
|
||||||
public:
|
public:
|
||||||
JumpEvent(Context* c, lir::UnaryOperation type, Value* address, bool exit,
|
JumpEvent(Context* c, lir::UnaryOperation type, Value* address, bool exit,
|
||||||
|
@ -1174,6 +1174,241 @@ appendMemory(Context* c, Value* base, int displacement, Value* index,
|
|||||||
MemoryEvent(c, base, displacement, index, scale, result));
|
MemoryEvent(c, base, displacement, index, scale, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double asFloat(unsigned size, int64_t v) {
|
||||||
|
if (size == 4) {
|
||||||
|
return vm::bitsToFloat(v);
|
||||||
|
} else {
|
||||||
|
return vm::bitsToDouble(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
unordered(double a, double b)
|
||||||
|
{
|
||||||
|
return not (a >= b or a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
shouldJump(Context* c, lir::TernaryOperation type, unsigned size, int64_t b,
|
||||||
|
int64_t a)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case lir::JumpIfEqual:
|
||||||
|
return a == b;
|
||||||
|
|
||||||
|
case lir::JumpIfNotEqual:
|
||||||
|
return a != b;
|
||||||
|
|
||||||
|
case lir::JumpIfLess:
|
||||||
|
return a < b;
|
||||||
|
|
||||||
|
case lir::JumpIfGreater:
|
||||||
|
return a > b;
|
||||||
|
|
||||||
|
case lir::JumpIfLessOrEqual:
|
||||||
|
return a <= b;
|
||||||
|
|
||||||
|
case lir::JumpIfGreaterOrEqual:
|
||||||
|
return a >= b;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatEqual:
|
||||||
|
return asFloat(size, a) == asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatNotEqual:
|
||||||
|
return asFloat(size, a) != asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLess:
|
||||||
|
return asFloat(size, a) < asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreater:
|
||||||
|
return asFloat(size, a) > asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLessOrEqual:
|
||||||
|
return asFloat(size, a) <= asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreaterOrEqual:
|
||||||
|
return asFloat(size, a) >= asFloat(size, b);
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLessOrUnordered:
|
||||||
|
return asFloat(size, a) < asFloat(size, b)
|
||||||
|
or unordered(asFloat(size, a), asFloat(size, b));
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreaterOrUnordered:
|
||||||
|
return asFloat(size, a) > asFloat(size, b)
|
||||||
|
or unordered(asFloat(size, a), asFloat(size, b));
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLessOrEqualOrUnordered:
|
||||||
|
return asFloat(size, a) <= asFloat(size, b)
|
||||||
|
or unordered(asFloat(size, a), asFloat(size, b));
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreaterOrEqualOrUnordered:
|
||||||
|
return asFloat(size, a) >= asFloat(size, b)
|
||||||
|
or unordered(asFloat(size, a), asFloat(size, b));
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lir::TernaryOperation
|
||||||
|
thunkBranch(Context* c, lir::TernaryOperation type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case lir::JumpIfFloatEqual:
|
||||||
|
return lir::JumpIfEqual;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatNotEqual:
|
||||||
|
return lir::JumpIfNotEqual;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLess:
|
||||||
|
case lir::JumpIfFloatLessOrUnordered:
|
||||||
|
return lir::JumpIfLess;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreater:
|
||||||
|
case lir::JumpIfFloatGreaterOrUnordered:
|
||||||
|
return lir::JumpIfGreater;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatLessOrEqual:
|
||||||
|
case lir::JumpIfFloatLessOrEqualOrUnordered:
|
||||||
|
return lir::JumpIfLessOrEqual;
|
||||||
|
|
||||||
|
case lir::JumpIfFloatGreaterOrEqual:
|
||||||
|
case lir::JumpIfFloatGreaterOrEqualOrUnordered:
|
||||||
|
return lir::JumpIfGreaterOrEqual;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BranchEvent: public Event {
|
||||||
|
public:
|
||||||
|
BranchEvent(Context* c, lir::TernaryOperation type, unsigned size,
|
||||||
|
Value* first, Value* second, Value* address,
|
||||||
|
const SiteMask& firstLowMask,
|
||||||
|
const SiteMask& firstHighMask,
|
||||||
|
const SiteMask& secondLowMask,
|
||||||
|
const SiteMask& secondHighMask):
|
||||||
|
Event(c), type(type), size(size), first(first), second(second),
|
||||||
|
address(address)
|
||||||
|
{
|
||||||
|
this->addReads(c, first, size, firstLowMask, firstHighMask);
|
||||||
|
this->addReads(c, second, size, secondLowMask, secondHighMask);
|
||||||
|
|
||||||
|
uint8_t typeMask;
|
||||||
|
uint64_t registerMask;
|
||||||
|
c->arch->planDestination(type, size, 0, 0, size, 0, 0, vm::TargetBytesPerWord,
|
||||||
|
&typeMask, ®isterMask);
|
||||||
|
|
||||||
|
this->addRead(c, address, SiteMask(typeMask, registerMask, AnyFrameIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* name() {
|
||||||
|
return "BranchEvent";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void compile(Context* c) {
|
||||||
|
ConstantSite* firstConstant = findConstantSite(c, first);
|
||||||
|
ConstantSite* secondConstant = findConstantSite(c, second);
|
||||||
|
|
||||||
|
if (not this->isUnreachable()) {
|
||||||
|
if (firstConstant
|
||||||
|
and secondConstant
|
||||||
|
and firstConstant->value->resolved()
|
||||||
|
and secondConstant->value->resolved())
|
||||||
|
{
|
||||||
|
int64_t firstValue = firstConstant->value->value();
|
||||||
|
int64_t secondValue = secondConstant->value->value();
|
||||||
|
|
||||||
|
if (size > vm::TargetBytesPerWord) {
|
||||||
|
firstValue |= findConstantSite
|
||||||
|
(c, first->nextWord)->value->value() << 32;
|
||||||
|
secondValue |= findConstantSite
|
||||||
|
(c, second->nextWord)->value->value() << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldJump(c, type, size, firstValue, secondValue)) {
|
||||||
|
apply(c, lir::Jump, vm::TargetBytesPerWord, address->source, address->source);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
freezeSource(c, size, first);
|
||||||
|
freezeSource(c, size, second);
|
||||||
|
freezeSource(c, vm::TargetBytesPerWord, address);
|
||||||
|
|
||||||
|
apply(c, type, size, first->source, first->nextWord->source,
|
||||||
|
size, second->source, second->nextWord->source,
|
||||||
|
vm::TargetBytesPerWord, address->source, address->source);
|
||||||
|
|
||||||
|
thawSource(c, vm::TargetBytesPerWord, address);
|
||||||
|
thawSource(c, size, second);
|
||||||
|
thawSource(c, size, first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Read* r = reads; r; r = r->eventNext) {
|
||||||
|
popRead(c, this, r->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isBranch() { return true; }
|
||||||
|
|
||||||
|
lir::TernaryOperation type;
|
||||||
|
unsigned size;
|
||||||
|
Value* first;
|
||||||
|
Value* second;
|
||||||
|
Value* address;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
appendBranch(Context* c, lir::TernaryOperation type, unsigned size, Value* first,
|
||||||
|
Value* second, Value* address)
|
||||||
|
{
|
||||||
|
bool thunk;
|
||||||
|
uint8_t firstTypeMask;
|
||||||
|
uint64_t firstRegisterMask;
|
||||||
|
uint8_t secondTypeMask;
|
||||||
|
uint64_t secondRegisterMask;
|
||||||
|
|
||||||
|
c->arch->planSource(type, size, &firstTypeMask, &firstRegisterMask,
|
||||||
|
size, &secondTypeMask, &secondRegisterMask,
|
||||||
|
vm::TargetBytesPerWord, &thunk);
|
||||||
|
|
||||||
|
if (thunk) {
|
||||||
|
Stack* oldStack = c->stack;
|
||||||
|
|
||||||
|
bool threadParameter;
|
||||||
|
intptr_t handler = c->client->getThunk
|
||||||
|
(type, size, size, &threadParameter);
|
||||||
|
|
||||||
|
assert(c, not threadParameter);
|
||||||
|
|
||||||
|
compiler::push(c, vm::ceilingDivide(size, vm::TargetBytesPerWord), second);
|
||||||
|
compiler::push(c, vm::ceilingDivide(size, vm::TargetBytesPerWord), first);
|
||||||
|
|
||||||
|
Stack* argumentStack = c->stack;
|
||||||
|
c->stack = oldStack;
|
||||||
|
|
||||||
|
Value* result = value(c, lir::ValueGeneral);
|
||||||
|
appendCall
|
||||||
|
(c, value
|
||||||
|
(c, lir::ValueGeneral, constantSite(c, handler)), 0, 0, result, 4,
|
||||||
|
argumentStack, vm::ceilingDivide(size, vm::TargetBytesPerWord) * 2, 0);
|
||||||
|
|
||||||
|
appendBranch(c, thunkBranch(c, type), 4, value
|
||||||
|
(c, lir::ValueGeneral, constantSite(c, static_cast<int64_t>(0))),
|
||||||
|
result, address);
|
||||||
|
} else {
|
||||||
|
append
|
||||||
|
(c, new(c->zone)
|
||||||
|
BranchEvent
|
||||||
|
(c, type, size, first, second, address,
|
||||||
|
SiteMask(firstTypeMask, firstRegisterMask, AnyFrameIndex),
|
||||||
|
SiteMask(firstTypeMask, firstRegisterMask >> 32, AnyFrameIndex),
|
||||||
|
SiteMask(secondTypeMask, secondRegisterMask, AnyFrameIndex),
|
||||||
|
SiteMask(secondTypeMask, secondRegisterMask >> 32, AnyFrameIndex)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace codegen
|
} // namespace codegen
|
||||||
} // namespace avian
|
} // namespace avian
|
||||||
|
@ -139,6 +139,10 @@ void
|
|||||||
appendMemory(Context* c, Value* base, int displacement, Value* index,
|
appendMemory(Context* c, Value* base, int displacement, Value* index,
|
||||||
unsigned scale, Value* result);
|
unsigned scale, Value* result);
|
||||||
|
|
||||||
|
void
|
||||||
|
appendBranch(Context* c, lir::TernaryOperation type, unsigned size, Value* first,
|
||||||
|
Value* second, Value* address);
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace codegen
|
} // namespace codegen
|
||||||
} // namespace avian
|
} // namespace avian
|
||||||
|
Loading…
Reference in New Issue
Block a user