cgen: merge byte-identical str+slice cgassign arms onto kind-gates -- Phase 2 C4.4
Post the str->24B lifts, cgassign had SEPARATE str and slice arms emitting byte-identical 3-word {ptr,len,cap} code. Collapse each identical pair into ONE kind-gated arm (rule 12, sea-of-stars; removes a drift hazard) -- the structural str==[]u8 unification, byte-id-NEUTRAL (each stage's emission unchanged for both str and slice inputs). Pairs: field store s.f=v + ident reassign name=v. cstage gates on the EXACT predicate union (raw kind==TY_STR OR'd with TY_SLICE -- NOT type_isstr, which would also match TY_UNTYPED_STR); ww on isstrtype||isslicetype and letvarisstr||letvarisslice (ww local field/reassign were already merged). Mirrors the in-tree deep-value-chain precedent (cstage 3274). str-only arms with no slice pair (arr[i].field=/chained, G1/G2) untouched.
Verified per-stage PRE==POST byte-identical (focused 5-path fixture + 4 large real combined.ww inputs, both stages); the 5 pairs were byte-identical pre-merge. main.combined.ww regenerated via the canonical make path (md5-stable). A pre-existing global-slice-field-store divergence (g.sl=b: cstage 3-word, wwstage 1-word) surfaced during review -- filed (#26/#10), NOT a C4.4 concern (PRE==POST).
This commit is contained in:
@@ -2587,42 +2587,18 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int is_global = (boff == 0 && !via_ptr
|
||||
&& let_islet(base->str));
|
||||
int foff = (int)f->offset;
|
||||
/* str IS []u8: rhs cgexpr leaves (AX=ptr, BX=len,
|
||||
* CX=cap); store all three at field+0/+8/+16,
|
||||
* mirroring the slice-field arm below. Address
|
||||
* scratch must dodge CX (holds cap), so via_ptr/
|
||||
* is_global stage the struct base in DX (#1/Phase 3).
|
||||
* Only plain `=` is wired; compound on a str field is
|
||||
* not meaningful. */
|
||||
Type *str_fu = (f->type && f->type->kind == TY_NAMED)
|
||||
? f->type->under : f->type;
|
||||
if (n->op == TK_ASSIGN && str_fu && str_fu->kind == TY_STR) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_DX, foff + 16));
|
||||
} else if (is_global) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_DX, foff + 16));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, boff + foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, boff + foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, boff + foff + 16));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* slice-typed field: rhs cgexpr leaves (AX=ptr,
|
||||
* BX=len, CX=cap); store all three at
|
||||
* field+0/+8/+16. Address scratch must dodge CX
|
||||
* (holds cap), so via_ptr/is_global stage the
|
||||
* struct base in DX. Without this branch the
|
||||
/* str/slice field: str IS []u8, so both store the full
|
||||
* 3-word {ptr,len,cap} that rhs cgexpr leaves in
|
||||
* (AX,BX,CX) at field+0/+8/+16. Address scratch must
|
||||
* dodge CX (holds cap), so via_ptr/is_global stage the
|
||||
* struct base in DX (#1/Phase 3). Without this the
|
||||
* generic store_op below writes only AX, silently
|
||||
* dropping .len and .cap. */
|
||||
if (n->op == TK_ASSIGN && str_fu && str_fu->kind == TY_SLICE) {
|
||||
* dropping .len/.cap. Only plain `=` is wired; compound
|
||||
* on a str/slice field is not meaningful. */
|
||||
if (n->op == TK_ASSIGN && str_fu
|
||||
&& (str_fu->kind == TY_SLICE || str_fu->kind == TY_STR)) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_DX));
|
||||
@@ -3850,41 +3826,18 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Plain `name = strexpr;` for a str-typed local. str IS []u8:
|
||||
* cgexpr leaves (AX=ptr, BX=len, CX=cap); store all three at
|
||||
* off+0/+8/+16, identical to the slice arm below. Top-level
|
||||
* str globals go through &name(SB) → DI scratch (CX holds cap)
|
||||
* since the asm has no `name+8(SB)` operand form (#1/Phase 3). */
|
||||
/* `name = expr;` reassignment of a str/slice/struct local or
|
||||
* top-level let. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->op == TK_ASSIGN
|
||||
&& n->lhs->type) {
|
||||
Type *lt = n->lhs->type;
|
||||
Type *lu = (lt && lt->kind == TY_NAMED) ? lt->under : lt;
|
||||
if (lu && lu->kind == TY_STR) {
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
if (off != 0) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
if (let_islet(n->lhs->str)) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins2(c, A_MOVQ, areg(D_CX), areg(D_DI));
|
||||
ins2(c, A_LEAQ, masym(c, n->lhs->str),
|
||||
areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_CX, 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_CX, 8));
|
||||
ins2(c, A_MOVQ, areg(D_DI), amem(D_CX, 16));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Slice reassignment: cgexpr produces (AX=ptr, BX=len,
|
||||
* CX=cap). Store all three at off+0/+8/+16 (local) or
|
||||
* via &name(SB) → DI scratch (global — CX holds the
|
||||
* cap, so we need a different address register). */
|
||||
if (lu && lu->kind == TY_SLICE) {
|
||||
/* str/slice local/let: str IS []u8, so both store the full
|
||||
* 3-word {ptr,len,cap} from (AX,BX,CX) at off+0/+8/+16
|
||||
* (local) or via &name(SB) → DI scratch (global — CX holds
|
||||
* the cap, and the asm has no `name+8(SB)` operand form, so
|
||||
* a different address register is needed) (#1/Phase 3). */
|
||||
if (lu && (lu->kind == TY_SLICE || lu->kind == TY_STR)) {
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
if (off != 0) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
|
||||
@@ -18213,33 +18213,12 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
};
|
||||
// str IS []u8: rhs left (AX=ptr,
|
||||
// BX=len, CX=cap). CX holds cap, so
|
||||
// stage the struct addr in DX and
|
||||
// store all three words — identical
|
||||
// to the slice arm below (#1/Phase 3).
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field via *struct: str IS []u8, so both
|
||||
// store the full 3-word {ptr,len,cap} from (AX,BX,CX).
|
||||
// CX holds cap, so stage the struct addr in DX and
|
||||
// store at foff/+8/+16 (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
@@ -18421,29 +18400,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};};
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
// str IS []u8: cgexpr left (AX=ptr,
|
||||
// BX=len, CX=cap); store all three at
|
||||
// +0/+8/+16, identical to the slice
|
||||
// arm below. BP base, no scratch
|
||||
// reload needed (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field direct: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
|
||||
// BP base, no scratch reload needed; the generic fldstoreop
|
||||
// below would write only AX, dropping .len/.cap (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
@@ -19466,21 +19427,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
// str IS []u8: stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap —
|
||||
// identical to the slice arm below
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
// str/slice top-level let: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap}. Stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap via &name(SB)
|
||||
// (#1/Phase 3).
|
||||
if (letvarisstr(c, nm) || letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
|
||||
@@ -4380,33 +4380,12 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
};
|
||||
// str IS []u8: rhs left (AX=ptr,
|
||||
// BX=len, CX=cap). CX holds cap, so
|
||||
// stage the struct addr in DX and
|
||||
// store all three words — identical
|
||||
// to the slice arm below (#1/Phase 3).
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field via *struct: str IS []u8, so both
|
||||
// store the full 3-word {ptr,len,cap} from (AX,BX,CX).
|
||||
// CX holds cap, so stage the struct addr in DX and
|
||||
// store at foff/+8/+16 (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
@@ -4588,29 +4567,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};};
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
// str IS []u8: cgexpr left (AX=ptr,
|
||||
// BX=len, CX=cap); store all three at
|
||||
// +0/+8/+16, identical to the slice
|
||||
// arm below. BP base, no scratch
|
||||
// reload needed (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field direct: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
|
||||
// BP base, no scratch reload needed; the generic fldstoreop
|
||||
// below would write only AX, dropping .len/.cap (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
@@ -5633,21 +5594,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
// str IS []u8: stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap —
|
||||
// identical to the slice arm below
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
// str/slice top-level let: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap}. Stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap via &name(SB)
|
||||
// (#1/Phase 3).
|
||||
if (letvarisstr(c, nm) || letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
|
||||
@@ -18213,33 +18213,12 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
};
|
||||
// str IS []u8: rhs left (AX=ptr,
|
||||
// BX=len, CX=cap). CX holds cap, so
|
||||
// stage the struct addr in DX and
|
||||
// store all three words — identical
|
||||
// to the slice arm below (#1/Phase 3).
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
// slice field via *struct: rhs left
|
||||
// (AX=ptr, BX=len, CX=cap). CX is
|
||||
// taken, so stage the struct addr
|
||||
// in DX. Store all three words at
|
||||
// foff/+8/+16.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field via *struct: str IS []u8, so both
|
||||
// store the full 3-word {ptr,len,cap} from (AX,BX,CX).
|
||||
// CX holds cap, so stage the struct addr in DX and
|
||||
// store at foff/+8/+16 (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), DX\n");
|
||||
@@ -18421,29 +18400,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};};
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
// str IS []u8: cgexpr left (AX=ptr,
|
||||
// BX=len, CX=cap); store all three at
|
||||
// +0/+8/+16, identical to the slice
|
||||
// arm below. BP base, no scratch
|
||||
// reload needed (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap); store all
|
||||
// three at +0/+8/+16. The generic
|
||||
// fldstoreop below would only write AX,
|
||||
// dropping .len/.cap.
|
||||
if (isslicetype(c, fi.tnode)) {
|
||||
// str/slice field direct: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
|
||||
// BP base, no scratch reload needed; the generic fldstoreop
|
||||
// below would write only AX, dropping .len/.cap (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
@@ -19466,21 +19427,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
// str IS []u8: stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap —
|
||||
// identical to the slice arm below
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
// str/slice top-level let: str IS []u8, so both store the
|
||||
// full 3-word {ptr,len,cap}. Stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap via &name(SB)
|
||||
// (#1/Phase 3).
|
||||
if (letvarisstr(c, nm) || letvarisslice(c, nm)) {
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
|
||||
Reference in New Issue
Block a user