How to reference subsets of logic[31:0] in SystemVerilog?
Is there a more elegant way I can have a simple logic [31:0] variable that can be easily referred to by various subsets of its wires without going through all of this verbose rigamarole?
  Is there a syntax I can use with Quartus II and ModelSim that allows me to avoid having yet another extra variable when sending the struct instruction_r (or i or j) to a module that expects a logic [31:0]?
  


The answer to both is yes. Simple data types such as logic [31:0] and struct packed {...} can be directly assigned; casting isn't necessary especially if they are the same width. 

Since everything is the same size (32 bits), direct assignment is allowed (example1):

instruction_r instr_r;
instruction_i instr_i;
instruction_j instr_j;

floper #(32) instruction_reg_r(.en(c_irwrite), .d(readdata), .q(instr_r), .* );

always_comb begin
  instr_i = instr_r;
  instr_j = instr_r;
end


The curly brackets ({}) can even be used (example2):

logic [5:0] op;
logic [25:0] addr;
logic [4:0] rs, rt;
logic [15:0] imm;
logic [4:0] rd, shamt;
logic [5:0] funct;

floper #(32) instruction_reg_r(.en(c_irwrite), .d(readdata), .q({op,addr}), .* );

always_comb begin
  {rs,rt,rd,shamt,funct} = addr;
  imm = {rd,shamt,funct};
end


Another approach is to use type parameters. See IEEE Std 1800-2012 &sect; 6.20.3 Type parameters. In this case change the definition of floper to:

module floper #(parameter WIDTH = 8, parameter type INSTR_TYPE = logic[WIDTH-1:0] )
               (input  logic             clk, reset, en,
                input  logic [WIDTH-1:0] d, 
                output INSTR_TYPE        q);
  // ...
endmodule


Then update the parameter value in the instantiations of floper. example3:

instruction_r instr_r;
instruction_i instr_i;
instruction_j instr_j;

floper #(32,instruction_r) instruction_reg_r(.en(c_irwrite), .d(readdata), .q(instr_r), .* );
floper #(32,instruction_i) instruction_reg_i(.en(c_irwrite), .d(readdata), .q(instr_i), .* );
floper #(32,instruction_j) instruction_reg_j(.en(c_irwrite), .d(readdata), .q(instr_j), .* );


Streaming operators ({&lt;&lt;{}} and {&gt;&gt;{}}) are needed when translating between packed/unpacked values, see IEEE Std 1800-2012 &sect; 11.4.14 Streaming operators, Casting is required for assignments that do not have a 1-to-1 relationship. Casting can also be used for tuncating or padding which is useful for avoiding size mismatch warnings. IEEE Std 1800-2012 &sect; 6.24 Casting a full description and examples.