}
 
 static struct ir3_instruction *
-get_frag_coord(struct ir3_context *ctx)
+get_frag_coord(struct ir3_context *ctx, nir_intrinsic_instr *intr)
 {
        if (!ctx->frag_coord) {
                struct ir3_block *b = ctx->in_block;
                }
 
                ctx->frag_coord = ir3_create_collect(ctx, xyzw, 4);
-               ctx->so->frag_coord = true;
        }
 
+       ctx->so->fragcoord_compmask |=
+                       nir_ssa_def_components_read(&intr->dest.ssa);
+
        return ctx->frag_coord;
 }
 
                emit_intrinsic_load_ubo_ldc(ctx, intr, dst);
                break;
        case nir_intrinsic_load_frag_coord:
-               ir3_split_dest(b, dst, get_frag_coord(ctx), 0, 4);
+               ir3_split_dest(b, dst, get_frag_coord(ctx, intr), 0, 4);
                break;
        case nir_intrinsic_load_sample_pos_from_id: {
                /* NOTE: blob seems to always use TYPE_F16 and then cov.f16f32,
 
         *   + From the vert shader, we only need the output regid
         */
 
-       bool frag_coord, frag_face, color0_mrt;
+       bool frag_face, color0_mrt;
+       uint8_t fragcoord_compmask;
 
        /* NOTE: for input/outputs, slot is:
         *   gl_vert_attrib  - for VS inputs
 
                        controlling blend or readback from GMEM??
                 -->
                <bitfield name="ENABLE_GMEM" pos="13" type="boolean"/>
-               <bitfield name="XCOORD" pos="14" type="boolean"/>
-               <bitfield name="YCOORD" pos="15" type="boolean"/>
-               <bitfield name="ZCOORD" pos="16" type="boolean"/>
-               <bitfield name="WCOORD" pos="17" type="boolean"/>
+               <bitfield name="COORD_MASK" low="14" high="17" type="hex"/>
                <bitfield name="I_CLAMP_ENABLE" pos="19" type="boolean"/>
                <bitfield name="COV_VALUE_OUTPUT_ENABLE" pos="20" type="boolean"/>
                <bitfield name="ALPHA_TEST" pos="22" type="boolean"/>
 
                <bitfield name="SAMPLES" low="13" high="15" type="uint"/>
        </reg32>
        <reg32 offset="0x20a3" name="RB_RENDER_CONTROL2">
-               <bitfield name="XCOORD" pos="0" type="boolean"/>
-               <bitfield name="YCOORD" pos="1" type="boolean"/>
-               <!-- assuming zcoord/wcoord follows.. -->
-               <bitfield name="ZCOORD" pos="2" type="boolean"/>
-               <bitfield name="WCOORD" pos="3" type="boolean"/>
+               <bitfield name="COORD_MASK" low="0" high="3" type="hex"/>
                <bitfield name="SAMPLEMASK" pos="4" type="boolean"/>
                <bitfield name="FACENESS" pos="5" type="boolean"/>
                <bitfield name="SAMPLEID" pos="6" type="boolean"/>
 
                Also, when that happens, VARYING bits are turned on as well.
                 -->
                <bitfield name="UNK3" pos="3" type="boolean"/>
-               <bitfield name="XCOORD" pos="6" type="boolean"/>
-               <bitfield name="YCOORD" pos="7" type="boolean"/>
-               <bitfield name="ZCOORD" pos="8" type="boolean"/>
-               <bitfield name="WCOORD" pos="9" type="boolean"/>
+               <bitfield name="COORD_MASK" low="6" high="9" type="hex"/>
        </reg32>
        <reg32 offset="0xe006" name="GRAS_CL_GUARDBAND_CLIP_ADJ">
                <bitfield name="HORZ" low="0" high="9" type="uint"/>
                Also, when that happens, VARYING bits are turned on as well.
                 -->
                <bitfield name="UNK3" pos="3" type="boolean"/>
-               <bitfield name="XCOORD" pos="6" type="boolean"/>
-               <bitfield name="YCOORD" pos="7" type="boolean"/>
-               <bitfield name="ZCOORD" pos="8" type="boolean"/>
-               <bitfield name="WCOORD" pos="9" type="boolean"/>
+               <bitfield name="COORD_MASK" low="6" high="9" type="hex"/>
        </reg32>
        <reg32 offset="0xe145" name="RB_RENDER_CONTROL1">
                <bitfield name="SAMPLEMASK" pos="0" type="boolean"/>
 
                <bitfield name="SIZE" pos="3" type="boolean"/>
                <!-- b5 set ofr interpolateAt{Offset,Sample}() if in per-sample mode -->
                <bitfield name="SIZE_PERSAMP" pos="5" type="boolean"/>
-               <bitfield name="XCOORD" pos="6" type="boolean"/>
-               <bitfield name="YCOORD" pos="7" type="boolean"/>
-               <bitfield name="ZCOORD" pos="8" type="boolean"/>
-               <bitfield name="WCOORD" pos="9" type="boolean"/>
+               <bitfield name="COORD_MASK" low="6" high="9" type="hex"/>
        </reg32>
        <reg32 offset="0x8006" name="GRAS_CL_GUARDBAND_CLIP_ADJ">
                <bitfield name="HORZ" low="0" high="9" type="uint"/>
                <bitfield name="SIZE" pos="3" type="boolean"/>
                <!-- b5 set ofr interpolateAt{Offset,Sample}() if in per-sample mode -->
                <bitfield name="SIZE_PERSAMP" pos="5" type="boolean"/>
-               <bitfield name="XCOORD" pos="6" type="boolean"/>
-               <bitfield name="YCOORD" pos="7" type="boolean"/>
-               <bitfield name="ZCOORD" pos="8" type="boolean"/>
-               <bitfield name="WCOORD" pos="9" type="boolean"/>
+               <bitfield name="COORD_MASK" low="6" high="9" type="hex"/>
                <bitfield name="UNK10" pos="10" type="boolean"/>
        </reg32>
        <reg32 offset="0x880a" name="RB_RENDER_CONTROL1">
 
          CONDREG(ij_samp_regid, A6XX_GRAS_CNTL_PERSAMP_VARYING) |
          COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_GRAS_CNTL_SIZE) |
          COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_GRAS_CNTL_SIZE_PERSAMP) |
-         COND(fs->frag_coord,
-               A6XX_GRAS_CNTL_SIZE |
-               A6XX_GRAS_CNTL_XCOORD |
-               A6XX_GRAS_CNTL_YCOORD |
-               A6XX_GRAS_CNTL_ZCOORD |
-               A6XX_GRAS_CNTL_WCOORD) |
+         COND(fs->fragcoord_compmask != 0, A6XX_GRAS_CNTL_SIZE |
+                              A6XX_GRAS_CNTL_COORD_MASK(fs->fragcoord_compmask)) |
          COND(fs->frag_face, A6XX_GRAS_CNTL_SIZE));
 
    tu_cs_emit_pkt4(cs, REG_A6XX_RB_RENDER_CONTROL0, 2);
          COND(enable_varyings, A6XX_RB_RENDER_CONTROL0_UNK10) |
          COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE) |
          COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE_PERSAMP) |
-         COND(fs->frag_coord,
-               A6XX_RB_RENDER_CONTROL0_SIZE |
-               A6XX_RB_RENDER_CONTROL0_XCOORD |
-               A6XX_RB_RENDER_CONTROL0_YCOORD |
-               A6XX_RB_RENDER_CONTROL0_ZCOORD |
-               A6XX_RB_RENDER_CONTROL0_WCOORD) |
+         COND(fs->fragcoord_compmask != 0, A6XX_RB_RENDER_CONTROL0_SIZE |
+                              A6XX_RB_RENDER_CONTROL0_COORD_MASK(fs->fragcoord_compmask)) |
          COND(fs->frag_face, A6XX_RB_RENDER_CONTROL0_SIZE));
    tu_cs_emit(cs,
          CONDREG(smask_in_regid, A6XX_RB_RENDER_CONTROL1_SAMPLEMASK) |
 
                        fd3_blend_stateobj(ctx->blend)->rb_render_control;
 
                val |= COND(fp->frag_face, A3XX_RB_RENDER_CONTROL_FACENESS);
-               val |= COND(fp->frag_coord, A3XX_RB_RENDER_CONTROL_XCOORD |
-                               A3XX_RB_RENDER_CONTROL_YCOORD |
-                               A3XX_RB_RENDER_CONTROL_ZCOORD |
-                               A3XX_RB_RENDER_CONTROL_WCOORD);
+               val |= COND(fp->fragcoord_compmask != 0,
+                               A3XX_RB_RENDER_CONTROL_COORD_MASK(fp->fragcoord_compmask));
 
                /* I suppose if we needed to (which I don't *think* we need
                 * to), we could emit this for binning pass too.  But we
                                ->gras_cl_clip_cntl;
                uint8_t planes = ctx->rasterizer->clip_plane_enable;
                val |= COND(fp->writes_pos, A3XX_GRAS_CL_CLIP_CNTL_ZCLIP_DISABLE);
-               val |= COND(fp->frag_coord, A3XX_GRAS_CL_CLIP_CNTL_ZCOORD |
+               val |= COND(fp->fragcoord_compmask != 0, A3XX_GRAS_CL_CLIP_CNTL_ZCOORD |
                                A3XX_GRAS_CL_CLIP_CNTL_WCOORD);
                if (!emit->key.ucp_enables)
                        val |= A3XX_GRAS_CL_CLIP_CNTL_NUM_USER_CLIP_PLANES(
 
                OUT_RING(ring, zsa->rb_depth_control |
                                COND(clamp, A4XX_RB_DEPTH_CONTROL_Z_CLAMP_ENABLE) |
                                COND(fragz, A4XX_RB_DEPTH_CONTROL_EARLY_Z_DISABLE) |
-                               COND(fragz && fp->frag_coord, A4XX_RB_DEPTH_CONTROL_FORCE_FRAGZ_TO_FS));
+                               COND(fragz && fp->fragcoord_compmask != 0,
+                                               A4XX_RB_DEPTH_CONTROL_FORCE_FRAGZ_TO_FS));
 
                /* maybe this register/bitfield needs a better name.. this
                 * appears to be just disabling early-z
                OUT_PKT0(ring, REG_A4XX_GRAS_ALPHA_CONTROL, 1);
                OUT_RING(ring, zsa->gras_alpha_control |
                                COND(fragz, A4XX_GRAS_ALPHA_CONTROL_ALPHA_TEST_ENABLE) |
-                               COND(fragz && fp->frag_coord, A4XX_GRAS_ALPHA_CONTROL_FORCE_FRAGZ_TO_FS));
+                               COND(fragz && fp->fragcoord_compmask != 0,
+                                               A4XX_GRAS_ALPHA_CONTROL_FORCE_FRAGZ_TO_FS));
        }
 
        if (dirty & FD_DIRTY_RASTERIZER) {
 
                                0x80000000 |      /* XXX */
                                COND(s[FS].v->frag_face, A4XX_SP_FS_CTRL_REG1_FACENESS) |
                                COND(s[FS].v->total_in > 0, A4XX_SP_FS_CTRL_REG1_VARYING) |
-                               COND(s[FS].v->frag_coord, A4XX_SP_FS_CTRL_REG1_FRAGCOORD));
+                               COND(s[FS].v->fragcoord_compmask != 0, A4XX_SP_FS_CTRL_REG1_FRAGCOORD));
 
                OUT_PKT0(ring, REG_A4XX_SP_FS_OBJ_OFFSET_REG, 2);
                OUT_RING(ring, A4XX_SP_FS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET(s[FS].constoff) |
        OUT_RING(ring, A4XX_RB_RENDER_CONTROL2_MSAA_SAMPLES(0) |
                        COND(s[FS].v->total_in > 0, A4XX_RB_RENDER_CONTROL2_VARYING) |
                        COND(s[FS].v->frag_face, A4XX_RB_RENDER_CONTROL2_FACENESS) |
-                       COND(s[FS].v->frag_coord, A4XX_RB_RENDER_CONTROL2_XCOORD |
-                                       A4XX_RB_RENDER_CONTROL2_YCOORD |
-                                       A4XX_RB_RENDER_CONTROL2_ZCOORD |
-                                       A4XX_RB_RENDER_CONTROL2_WCOORD));
+                       COND(s[FS].v->fragcoord_compmask != 0,
+                                       A4XX_RB_RENDER_CONTROL2_COORD_MASK(s[FS].v->fragcoord_compmask)));
 
        OUT_PKT0(ring, REG_A4XX_RB_FS_OUTPUT_REG, 1);
        OUT_RING(ring, A4XX_RB_FS_OUTPUT_REG_MRT(nr) |
 
 
                OUT_PKT4(ring, REG_A5XX_RB_DEPTH_PLANE_CNTL, 1);
                OUT_RING(ring, COND(fragz, A5XX_RB_DEPTH_PLANE_CNTL_FRAG_WRITES_Z) |
-                               COND(fragz && fp->frag_coord, A5XX_RB_DEPTH_PLANE_CNTL_UNK1));
+                               COND(fragz && fp->fragcoord_compmask != 0,
+                               A5XX_RB_DEPTH_PLANE_CNTL_UNK1));
 
                OUT_PKT4(ring, REG_A5XX_GRAS_SU_DEPTH_PLANE_CNTL, 1);
                OUT_RING(ring, COND(fragz, A5XX_GRAS_SU_DEPTH_PLANE_CNTL_FRAG_WRITES_Z) |
-                               COND(fragz && fp->frag_coord, A5XX_GRAS_SU_DEPTH_PLANE_CNTL_UNK1));
+                               COND(fragz && fp->fragcoord_compmask != 0,
+                               A5XX_GRAS_SU_DEPTH_PLANE_CNTL_UNK1));
        }
 
        /* NOTE: scissor enabled bit is part of rasterizer state: */
 
        OUT_PKT4(ring, REG_A5XX_VPC_CNTL_0, 1);
        OUT_RING(ring, A5XX_VPC_CNTL_0_STRIDE_IN_VPC(l.max_loc) |
                        COND(s[FS].v->total_in > 0, A5XX_VPC_CNTL_0_VARYING) |
-                       COND(s[FS].v->frag_coord, A5XX_VPC_CNTL_0_VARYING) |
+                       COND(s[FS].v->fragcoord_compmask != 0, A5XX_VPC_CNTL_0_VARYING) |
                        0x10000);    // XXX
 
        fd5_context(ctx)->max_loc = l.max_loc;
 
        OUT_PKT4(ring, REG_A5XX_SP_FS_CTRL_REG0, 1);
        OUT_RING(ring, COND(s[FS].v->total_in > 0, A5XX_SP_FS_CTRL_REG0_VARYING) |
-                       COND(s[FS].v->frag_coord, A5XX_SP_FS_CTRL_REG0_VARYING) |
+                       COND(s[FS].v->fragcoord_compmask != 0, A5XX_SP_FS_CTRL_REG0_VARYING) |
                        0x40006 | /* XXX set pretty much everywhere */
                        A5XX_SP_FS_CTRL_REG0_THREADSIZE(fssz) |
                        A5XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT(s[FS].i->max_half_reg + 1) |
 
        OUT_PKT4(ring, REG_A5XX_GRAS_CNTL, 1);
        OUT_RING(ring, COND(s[FS].v->total_in > 0, A5XX_GRAS_CNTL_VARYING) |
-                       COND(s[FS].v->frag_coord, A5XX_GRAS_CNTL_XCOORD |
-                                       A5XX_GRAS_CNTL_YCOORD |
-                                       A5XX_GRAS_CNTL_ZCOORD |
-                                       A5XX_GRAS_CNTL_WCOORD |
+                       COND(s[FS].v->fragcoord_compmask != 0,
+                                       A5XX_GRAS_CNTL_COORD_MASK(s[FS].v->fragcoord_compmask) |
                                        A5XX_GRAS_CNTL_UNK3) |
                        COND(s[FS].v->frag_face, A5XX_GRAS_CNTL_UNK3));
 
        OUT_PKT4(ring, REG_A5XX_RB_RENDER_CONTROL0, 2);
        OUT_RING(ring, COND(s[FS].v->total_in > 0, A5XX_RB_RENDER_CONTROL0_VARYING) |
-                       COND(s[FS].v->frag_coord, A5XX_RB_RENDER_CONTROL0_XCOORD |
-                                       A5XX_RB_RENDER_CONTROL0_YCOORD |
-                                       A5XX_RB_RENDER_CONTROL0_ZCOORD |
-                                       A5XX_RB_RENDER_CONTROL0_WCOORD |
+                       COND(s[FS].v->fragcoord_compmask != 0,
+                                       A5XX_RB_RENDER_CONTROL0_COORD_MASK(s[FS].v->fragcoord_compmask) |
                                        A5XX_RB_RENDER_CONTROL0_UNK3) |
                        COND(s[FS].v->frag_face, A5XX_RB_RENDER_CONTROL0_UNK3));
        OUT_RING(ring,
 
                        CONDREG(ij_samp_regid, A6XX_GRAS_CNTL_PERSAMP_VARYING) |
                        COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_GRAS_CNTL_SIZE) |
                        COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_GRAS_CNTL_SIZE_PERSAMP) |
-                       COND(fs->frag_coord,
-                                       A6XX_GRAS_CNTL_SIZE |
-                                       A6XX_GRAS_CNTL_XCOORD |
-                                       A6XX_GRAS_CNTL_YCOORD |
-                                       A6XX_GRAS_CNTL_ZCOORD |
-                                       A6XX_GRAS_CNTL_WCOORD) |
+                       COND(fs->fragcoord_compmask != 0, A6XX_GRAS_CNTL_SIZE |
+                                                               A6XX_GRAS_CNTL_COORD_MASK(fs->fragcoord_compmask)) |
                        COND(fs->frag_face, A6XX_GRAS_CNTL_SIZE));
 
        OUT_PKT4(ring, REG_A6XX_RB_RENDER_CONTROL0, 2);
                        COND(enable_varyings, A6XX_RB_RENDER_CONTROL0_UNK10) |
                        COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE) |
                        COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE_PERSAMP) |
-                       COND(fs->frag_coord,
-                                       A6XX_RB_RENDER_CONTROL0_SIZE |
-                                       A6XX_RB_RENDER_CONTROL0_XCOORD |
-                                       A6XX_RB_RENDER_CONTROL0_YCOORD |
-                                       A6XX_RB_RENDER_CONTROL0_ZCOORD |
-                                       A6XX_RB_RENDER_CONTROL0_WCOORD) |
+                       COND(fs->fragcoord_compmask != 0, A6XX_RB_RENDER_CONTROL0_SIZE |
+                                                               A6XX_RB_RENDER_CONTROL0_COORD_MASK(fs->fragcoord_compmask)) |
                        COND(fs->frag_face, A6XX_RB_RENDER_CONTROL0_SIZE));
 
        OUT_RING(ring,