Here's an addendum. The style above only works for a 7-input mux, not a 5-input. For less than 7 inputs, Vivado still omits the F7. Here's the Vivado kludge:
wire [7:0] mux_inputs[0:4]; // only 5 inputs
wire [7:0] mux_out;
wire [2:0] mux_sel;
(*keep="true"*) wire [2:0] dummy_x[5:7];
assign dummy_x[5] = 3'bx;
assign dummy_x[6] = 3'bx;
assign dummy_x[7] = 3'bx;
always@(posedge clk)
if (mux_sel<5)
mux_out <= mux_inputs[mux_sel];
else
mux_out <= dummy_x[mux_sel];
After synthesis, Vivado uses extra LUTs to implement the KEEP dummy wires, but then it removes these in PAR.
This whole thing is ridiculous, but this is how you get Vivado to do what it's supposed to do.