Make KeyedLoads from a sloppy arguments array use a handler.
Before, a custom stub was installed. R=verwaest@chromium.org Review URL: https://codereview.chromium.org/546683003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24120 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
6ca218cf07
commit
6bb1340515
@ -1289,11 +1289,6 @@ static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_KeyedLoadIC_SloppyArguments(MacroAssembler* masm) {
|
||||
KeyedLoadIC::GenerateSloppyArguments(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_StoreIC_Miss(MacroAssembler* masm) {
|
||||
StoreIC::GenerateMiss(masm);
|
||||
}
|
||||
|
@ -89,7 +89,6 @@ enum BuiltinExtraArguments {
|
||||
kNoExtraICState) \
|
||||
V(KeyedLoadIC_Generic, KEYED_LOAD_IC, GENERIC, kNoExtraICState) \
|
||||
V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, kNoExtraICState) \
|
||||
V(KeyedLoadIC_SloppyArguments, KEYED_LOAD_IC, MONOMORPHIC, kNoExtraICState) \
|
||||
\
|
||||
V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, StoreIC::kStrictModeState) \
|
||||
\
|
||||
|
@ -71,6 +71,8 @@ class CodeStubGraphBuilderBase : public HGraphBuilder {
|
||||
MULTIPLE
|
||||
};
|
||||
|
||||
HValue* UnmappedCase(HValue* elements, HValue* key);
|
||||
|
||||
HValue* BuildArrayConstructor(ElementsKind kind,
|
||||
AllocationSiteOverrideMode override_mode,
|
||||
ArgumentClass argument_class);
|
||||
@ -600,6 +602,122 @@ HValue* CodeStubGraphBuilder<LoadConstantStub>::BuildCodeStub() {
|
||||
Handle<Code> LoadConstantStub::GenerateCode() { return DoGenerateCode(this); }
|
||||
|
||||
|
||||
HValue* CodeStubGraphBuilderBase::UnmappedCase(HValue* elements, HValue* key) {
|
||||
HValue* result;
|
||||
HInstruction* backing_store = Add<HLoadKeyed>(
|
||||
elements, graph()->GetConstant1(), static_cast<HValue*>(NULL),
|
||||
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
|
||||
Add<HCheckMaps>(backing_store, isolate()->factory()->fixed_array_map());
|
||||
HValue* backing_store_length =
|
||||
Add<HLoadNamedField>(backing_store, static_cast<HValue*>(NULL),
|
||||
HObjectAccess::ForFixedArrayLength());
|
||||
IfBuilder in_unmapped_range(this);
|
||||
in_unmapped_range.If<HCompareNumericAndBranch>(key, backing_store_length,
|
||||
Token::LT);
|
||||
in_unmapped_range.Then();
|
||||
{
|
||||
result = Add<HLoadKeyed>(backing_store, key, static_cast<HValue*>(NULL),
|
||||
FAST_HOLEY_ELEMENTS, NEVER_RETURN_HOLE);
|
||||
}
|
||||
in_unmapped_range.ElseDeopt("Outside of range");
|
||||
in_unmapped_range.End();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
HValue* CodeStubGraphBuilder<KeyedLoadSloppyArgumentsStub>::BuildCodeStub() {
|
||||
HValue* receiver = GetParameter(LoadDescriptor::kReceiverIndex);
|
||||
HValue* key = GetParameter(LoadDescriptor::kNameIndex);
|
||||
|
||||
// Mapped arguments are actual arguments. Unmapped arguments are values added
|
||||
// to the arguments object after it was created for the call. Mapped arguments
|
||||
// are stored in the context at indexes given by elements[key + 2]. Unmapped
|
||||
// arguments are stored as regular indexed properties in the arguments array,
|
||||
// held at elements[1]. See NewSloppyArguments() in runtime.cc for a detailed
|
||||
// look at argument object construction.
|
||||
//
|
||||
// The sloppy arguments elements array has a special format:
|
||||
//
|
||||
// 0: context
|
||||
// 1: unmapped arguments array
|
||||
// 2: mapped_index0,
|
||||
// 3: mapped_index1,
|
||||
// ...
|
||||
//
|
||||
// length is 2 + min(number_of_actual_arguments, number_of_formal_arguments).
|
||||
// If key + 2 >= elements.length then attempt to look in the unmapped
|
||||
// arguments array (given by elements[1]) and return the value at key, missing
|
||||
// to the runtime if the unmapped arguments array is not a fixed array or if
|
||||
// key >= unmapped_arguments_array.length.
|
||||
//
|
||||
// Otherwise, t = elements[key + 2]. If t is the hole, then look up the value
|
||||
// in the unmapped arguments array, as described above. Otherwise, t is a Smi
|
||||
// index into the context array given at elements[0]. Return the value at
|
||||
// context[t].
|
||||
|
||||
key = AddUncasted<HForceRepresentation>(key, Representation::Smi());
|
||||
IfBuilder positive_smi(this);
|
||||
positive_smi.If<HCompareNumericAndBranch>(key, graph()->GetConstant0(),
|
||||
Token::LT);
|
||||
positive_smi.ThenDeopt("key is negative");
|
||||
positive_smi.End();
|
||||
|
||||
HValue* constant_two = Add<HConstant>(2);
|
||||
HValue* elements = AddLoadElements(receiver, static_cast<HValue*>(NULL));
|
||||
HValue* elements_length =
|
||||
Add<HLoadNamedField>(elements, static_cast<HValue*>(NULL),
|
||||
HObjectAccess::ForFixedArrayLength());
|
||||
HValue* adjusted_length = AddUncasted<HSub>(elements_length, constant_two);
|
||||
IfBuilder in_range(this);
|
||||
in_range.If<HCompareNumericAndBranch>(key, adjusted_length, Token::LT);
|
||||
in_range.Then();
|
||||
{
|
||||
HValue* index = AddUncasted<HAdd>(key, constant_two);
|
||||
HInstruction* mapped_index =
|
||||
Add<HLoadKeyed>(elements, index, static_cast<HValue*>(NULL),
|
||||
FAST_HOLEY_ELEMENTS, ALLOW_RETURN_HOLE);
|
||||
|
||||
IfBuilder is_valid(this);
|
||||
is_valid.IfNot<HCompareObjectEqAndBranch>(mapped_index,
|
||||
graph()->GetConstantHole());
|
||||
is_valid.Then();
|
||||
{
|
||||
// TODO(mvstanton): I'd like to assert from this point, that if the
|
||||
// mapped_index is not the hole that it is indeed, a smi. An unnecessary
|
||||
// smi check is being emitted.
|
||||
HValue* the_context =
|
||||
Add<HLoadKeyed>(elements, graph()->GetConstant0(),
|
||||
static_cast<HValue*>(NULL), FAST_ELEMENTS);
|
||||
DCHECK(Context::kHeaderSize == FixedArray::kHeaderSize);
|
||||
HValue* result =
|
||||
Add<HLoadKeyed>(the_context, mapped_index, static_cast<HValue*>(NULL),
|
||||
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
|
||||
environment()->Push(result);
|
||||
}
|
||||
is_valid.Else();
|
||||
{
|
||||
HValue* result = UnmappedCase(elements, key);
|
||||
environment()->Push(result);
|
||||
}
|
||||
is_valid.End();
|
||||
}
|
||||
in_range.Else();
|
||||
{
|
||||
HValue* result = UnmappedCase(elements, key);
|
||||
environment()->Push(result);
|
||||
}
|
||||
in_range.End();
|
||||
|
||||
return environment()->Pop();
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedLoadSloppyArgumentsStub::GenerateCode() {
|
||||
return DoGenerateCode(this);
|
||||
}
|
||||
|
||||
|
||||
void CodeStubGraphBuilderBase::BuildStoreNamedField(
|
||||
HValue* object, HValue* value, FieldIndex index,
|
||||
Representation representation) {
|
||||
@ -1092,7 +1210,6 @@ Handle<Code> ToBooleanStub::GenerateCode() {
|
||||
template <>
|
||||
HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
|
||||
StoreGlobalStub* stub = casted_stub();
|
||||
Handle<Object> hole(isolate()->heap()->the_hole_value(), isolate());
|
||||
Handle<Object> placeholer_value(Smi::FromInt(0), isolate());
|
||||
Handle<PropertyCell> placeholder_cell =
|
||||
isolate()->factory()->NewPropertyCell(placeholer_value);
|
||||
@ -1124,7 +1241,7 @@ HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
|
||||
// property has been deleted and that the store must be handled by the
|
||||
// runtime.
|
||||
IfBuilder builder(this);
|
||||
HValue* hole_value = Add<HConstant>(hole);
|
||||
HValue* hole_value = graph()->GetConstantHole();
|
||||
builder.If<HCompareObjectEqAndBranch>(cell_contents, hole_value);
|
||||
builder.Then();
|
||||
builder.Deopt("Unexpected cell contents in global store");
|
||||
|
@ -586,12 +586,14 @@ void KeyedLoadGenericStub::InitializeDescriptor(
|
||||
void HandlerStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
||||
if (kind() == Code::STORE_IC) {
|
||||
descriptor->Initialize(FUNCTION_ADDR(StoreIC_MissFromStubFailure));
|
||||
} else if (kind() == Code::KEYED_LOAD_IC) {
|
||||
descriptor->Initialize(FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CallInterfaceDescriptor HandlerStub::GetCallInterfaceDescriptor() {
|
||||
if (kind() == Code::LOAD_IC) {
|
||||
if (kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC) {
|
||||
return LoadDescriptor(isolate());
|
||||
} else {
|
||||
DCHECK_EQ(Code::STORE_IC, kind());
|
||||
|
@ -82,6 +82,7 @@ namespace internal {
|
||||
/* IC Handler stubs */ \
|
||||
V(LoadConstant) \
|
||||
V(LoadField) \
|
||||
V(KeyedLoadSloppyArguments) \
|
||||
V(StoreField) \
|
||||
V(StoreGlobal) \
|
||||
V(StringLength)
|
||||
@ -914,6 +915,20 @@ class LoadFieldStub: public HandlerStub {
|
||||
};
|
||||
|
||||
|
||||
class KeyedLoadSloppyArgumentsStub : public HandlerStub {
|
||||
public:
|
||||
explicit KeyedLoadSloppyArgumentsStub(Isolate* isolate)
|
||||
: HandlerStub(isolate) {}
|
||||
|
||||
protected:
|
||||
virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; }
|
||||
virtual Code::StubType GetStubType() { return Code::FAST; }
|
||||
|
||||
private:
|
||||
DEFINE_HANDLER_CODE_STUB(KeyedLoadSloppyArguments, HandlerStub);
|
||||
};
|
||||
|
||||
|
||||
class LoadConstantStub : public HandlerStub {
|
||||
public:
|
||||
LoadConstantStub(Isolate* isolate, int constant_index)
|
||||
|
@ -87,6 +87,11 @@ inline bool IsDictionaryElementsKind(ElementsKind kind) {
|
||||
}
|
||||
|
||||
|
||||
inline bool IsSloppyArgumentsElements(ElementsKind kind) {
|
||||
return kind == SLOPPY_ARGUMENTS_ELEMENTS;
|
||||
}
|
||||
|
||||
|
||||
inline bool IsExternalArrayElementsKind(ElementsKind kind) {
|
||||
return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
|
||||
kind <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND;
|
||||
|
@ -369,32 +369,6 @@ static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(r1));
|
||||
DCHECK(key.is(r2));
|
||||
|
||||
Label slow, notin;
|
||||
MemOperand mapped_location = GenerateMappedArgumentsLookup(
|
||||
masm, receiver, key, r0, r3, r4, ¬in, &slow);
|
||||
__ ldr(r0, mapped_location);
|
||||
__ Ret();
|
||||
__ bind(¬in);
|
||||
// The unmapped lookup expects that the parameter map is in r0.
|
||||
MemOperand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, key, r0, r3, &slow);
|
||||
__ ldr(r0, unmapped_location);
|
||||
__ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
|
||||
__ cmp(r0, r3);
|
||||
__ b(eq, &slow);
|
||||
__ Ret();
|
||||
__ bind(&slow);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register key = StoreDescriptor::NameRegister();
|
||||
|
@ -370,35 +370,6 @@ void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
Register result = x0;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(x1));
|
||||
DCHECK(key.is(x2));
|
||||
|
||||
Label miss, unmapped;
|
||||
|
||||
Register map_scratch = x0;
|
||||
MemOperand mapped_location = GenerateMappedArgumentsLookup(
|
||||
masm, receiver, key, map_scratch, x3, x4, &unmapped, &miss);
|
||||
__ Ldr(result, mapped_location);
|
||||
__ Ret();
|
||||
|
||||
__ Bind(&unmapped);
|
||||
// Parameter map is left in map_scratch when a jump on unmapped is done.
|
||||
MemOperand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, key, map_scratch, x3, &miss);
|
||||
__ Ldr(result, unmapped_location);
|
||||
__ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss);
|
||||
__ Ret();
|
||||
|
||||
__ Bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
ASM_LOCATION("KeyedStoreIC::GenerateSloppyArguments");
|
||||
Label slow, notin;
|
||||
|
@ -390,13 +390,13 @@ void ElementHandlerCompiler::CompileElementHandlers(
|
||||
ElementsKind elements_kind = receiver_map->elements_kind();
|
||||
if (receiver_map->has_indexed_interceptor()) {
|
||||
cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode();
|
||||
} else if (IsSloppyArgumentsElements(elements_kind)) {
|
||||
cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode();
|
||||
} else if (IsFastElementsKind(elements_kind) ||
|
||||
IsExternalArrayElementsKind(elements_kind) ||
|
||||
IsFixedTypedArrayElementsKind(elements_kind)) {
|
||||
cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind)
|
||||
.GetCode();
|
||||
} else if (elements_kind == SLOPPY_ARGUMENTS_ELEMENTS) {
|
||||
cached_stub = isolate()->builtins()->KeyedLoadIC_SloppyArguments();
|
||||
} else {
|
||||
DCHECK(elements_kind == DICTIONARY_ELEMENTS);
|
||||
cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
|
||||
|
@ -503,32 +503,6 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is on the stack.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(edx));
|
||||
DCHECK(key.is(ecx));
|
||||
|
||||
Label slow, notin;
|
||||
Factory* factory = masm->isolate()->factory();
|
||||
Operand mapped_location = GenerateMappedArgumentsLookup(
|
||||
masm, receiver, key, ebx, eax, ¬in, &slow);
|
||||
__ mov(eax, mapped_location);
|
||||
__ Ret();
|
||||
__ bind(¬in);
|
||||
// The unmapped lookup expects that the parameter map is in ebx.
|
||||
Operand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, key, ebx, eax, &slow);
|
||||
__ cmp(unmapped_location, factory->the_hole_value());
|
||||
__ j(equal, &slow);
|
||||
__ mov(eax, unmapped_location);
|
||||
__ Ret();
|
||||
__ bind(&slow);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
Label slow, notin;
|
||||
|
@ -96,6 +96,8 @@ Handle<Code> PropertyICCompiler::ComputeKeyedLoadMonomorphic(
|
||||
Handle<Code> stub;
|
||||
if (receiver_map->has_indexed_interceptor()) {
|
||||
stub = LoadIndexedInterceptorStub(isolate).GetCode();
|
||||
} else if (receiver_map->has_sloppy_arguments_elements()) {
|
||||
stub = KeyedLoadSloppyArgumentsStub(isolate).GetCode();
|
||||
} else if (receiver_map->has_fast_elements() ||
|
||||
receiver_map->has_external_array_elements() ||
|
||||
receiver_map->has_fixed_typed_array_elements()) {
|
||||
|
@ -1187,11 +1187,7 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
|
||||
if (state() == UNINITIALIZED) stub = string_stub();
|
||||
} else if (object->IsJSObject()) {
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||
if (receiver->elements()->map() ==
|
||||
isolate()->heap()->sloppy_arguments_elements_map()) {
|
||||
stub = sloppy_arguments_stub();
|
||||
} else if (!Object::ToSmi(isolate(), key).is_null() &&
|
||||
(!target().is_identical_to(sloppy_arguments_stub()))) {
|
||||
if (!Object::ToSmi(isolate(), key).is_null()) {
|
||||
stub = LoadElementStub(receiver);
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +414,6 @@ class KeyedLoadIC : public LoadIC {
|
||||
}
|
||||
static void GenerateGeneric(MacroAssembler* masm);
|
||||
static void GenerateString(MacroAssembler* masm);
|
||||
static void GenerateSloppyArguments(MacroAssembler* masm);
|
||||
|
||||
// Bit mask to be tested against bit field for the cases when
|
||||
// generic stub should go into slow case.
|
||||
@ -434,9 +433,6 @@ class KeyedLoadIC : public LoadIC {
|
||||
|
||||
private:
|
||||
Handle<Code> generic_stub() const { return generic_stub(isolate()); }
|
||||
Handle<Code> sloppy_arguments_stub() {
|
||||
return isolate()->builtins()->KeyedLoadIC_SloppyArguments();
|
||||
}
|
||||
Handle<Code> string_stub() {
|
||||
return isolate()->builtins()->KeyedLoadIC_String();
|
||||
}
|
||||
|
@ -374,32 +374,6 @@ static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is in ra.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(a1));
|
||||
DCHECK(key.is(a2));
|
||||
|
||||
Label slow, notin;
|
||||
MemOperand mapped_location = GenerateMappedArgumentsLookup(
|
||||
masm, receiver, key, a0, a3, t0, ¬in, &slow);
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ lw(v0, mapped_location);
|
||||
__ bind(¬in);
|
||||
// The unmapped lookup expects that the parameter map is in a0.
|
||||
MemOperand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, key, a0, a3, &slow);
|
||||
__ lw(a0, unmapped_location);
|
||||
__ LoadRoot(a3, Heap::kTheHoleValueRootIndex);
|
||||
__ Branch(&slow, eq, a0, Operand(a3));
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ mov(v0, a0);
|
||||
__ bind(&slow);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register key = StoreDescriptor::NameRegister();
|
||||
|
@ -722,31 +722,6 @@ static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is on the stack.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(rdx));
|
||||
DCHECK(key.is(rcx));
|
||||
|
||||
Label slow, notin;
|
||||
Operand mapped_location = GenerateMappedArgumentsLookup(
|
||||
masm, receiver, key, rbx, rax, rdi, ¬in, &slow);
|
||||
__ movp(rax, mapped_location);
|
||||
__ Ret();
|
||||
__ bind(¬in);
|
||||
// The unmapped lookup expects that the parameter map is in rbx.
|
||||
Operand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, key, rbx, rax, &slow);
|
||||
__ CompareRoot(unmapped_location, Heap::kTheHoleValueRootIndex);
|
||||
__ j(equal, &slow);
|
||||
__ movp(rax, unmapped_location);
|
||||
__ Ret();
|
||||
__ bind(&slow);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
|
||||
// The return address is on the stack.
|
||||
Label slow, notin;
|
||||
|
Loading…
Reference in New Issue
Block a user