@@ -276,6 +276,68 @@ static inline zend_bool is_var_dead(context *ctx, int var_num) {
276
276
}
277
277
}
278
278
279
+ // Sometimes we can mark the var as EXT_UNUSED
280
+ static zend_bool try_remove_var_def (context * ctx , int free_var , int use_chain , zend_op * opline ) {
281
+ if (use_chain >= 0 ) {
282
+ return 0 ;
283
+ }
284
+ zend_ssa_var * var = & ctx -> ssa -> vars [free_var ];
285
+ int def = var -> definition ;
286
+
287
+ if (def >= 0 ) {
288
+ zend_ssa_op * def_op = & ctx -> ssa -> ops [def ];
289
+
290
+ if (def_op -> result_def == free_var
291
+ && var -> phi_use_chain == NULL
292
+ && var -> use_chain == (opline - ctx -> op_array -> opcodes )) {
293
+ zend_op * def_opline = & ctx -> op_array -> opcodes [def ];
294
+
295
+ switch (def_opline -> opcode ) {
296
+ case ZEND_ASSIGN :
297
+ case ZEND_ASSIGN_REF :
298
+ case ZEND_ASSIGN_DIM :
299
+ case ZEND_ASSIGN_OBJ :
300
+ case ZEND_ASSIGN_ADD :
301
+ case ZEND_ASSIGN_SUB :
302
+ case ZEND_ASSIGN_MUL :
303
+ case ZEND_ASSIGN_DIV :
304
+ case ZEND_ASSIGN_MOD :
305
+ case ZEND_ASSIGN_SL :
306
+ case ZEND_ASSIGN_SR :
307
+ case ZEND_ASSIGN_CONCAT :
308
+ case ZEND_ASSIGN_BW_OR :
309
+ case ZEND_ASSIGN_BW_AND :
310
+ case ZEND_ASSIGN_BW_XOR :
311
+ case ZEND_ASSIGN_POW :
312
+ case ZEND_PRE_INC :
313
+ case ZEND_POST_INC :
314
+ case ZEND_PRE_DEC :
315
+ case ZEND_POST_DEC :
316
+ case ZEND_PRE_INC_OBJ :
317
+ case ZEND_POST_INC_OBJ :
318
+ case ZEND_PRE_DEC_OBJ :
319
+ case ZEND_POST_DEC_OBJ :
320
+ case ZEND_DO_ICALL :
321
+ case ZEND_DO_UCALL :
322
+ case ZEND_DO_FCALL_BY_NAME :
323
+ case ZEND_DO_FCALL :
324
+ case ZEND_INCLUDE_OR_EVAL :
325
+ case ZEND_YIELD :
326
+ case ZEND_YIELD_FROM :
327
+ case ZEND_ASSERT_CHECK :
328
+ def_opline -> result_type = IS_UNUSED ;
329
+ def_opline -> result .var = 0 ;
330
+ def_op -> result_def = -1 ;
331
+ var -> definition = -1 ;
332
+ return 1 ;
333
+ default :
334
+ break ;
335
+ }
336
+ }
337
+ }
338
+ return 0 ;
339
+ }
340
+
279
341
/* Returns whether the instruction has been DCEd */
280
342
static zend_bool dce_instr (context * ctx , zend_op * opline , zend_ssa_op * ssa_op ) {
281
343
zend_ssa * ssa = ctx -> ssa ;
@@ -291,20 +353,28 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
291
353
return 0 ;
292
354
}
293
355
294
- // TODO Two free vars?
295
356
if ((opline -> op1_type & (IS_VAR |IS_TMP_VAR )) && !is_var_dead (ctx , ssa_op -> op1_use )) {
296
- free_var = ssa_op -> op1_use ;
297
- free_var_type = opline -> op1_type ;
298
- } else if ((opline -> op2_type & (IS_VAR |IS_TMP_VAR )) && !is_var_dead (ctx , ssa_op -> op2_use )) {
299
- free_var = ssa_op -> op2_use ;
300
- free_var_type = opline -> op2_type ;
357
+ if (!try_remove_var_def (ctx , ssa_op -> op1_use , ssa_op -> op1_use_chain , opline )) {
358
+ free_var = ssa_op -> op1_use ;
359
+ free_var_type = opline -> op1_type ;
360
+ }
361
+ }
362
+ if ((opline -> op2_type & (IS_VAR |IS_TMP_VAR )) && !is_var_dead (ctx , ssa_op -> op2_use )) {
363
+ if (!try_remove_var_def (ctx , ssa_op -> op2_use , ssa_op -> op2_use_chain , opline )) {
364
+ if (free_var >= 0 ) {
365
+ // TODO: We can't free two vars. Keep instruction alive.
366
+ zend_bitset_excl (ctx -> instr_dead , opline - ctx -> op_array -> opcodes );
367
+ return 0 ;
368
+ }
369
+ free_var = ssa_op -> op2_use ;
370
+ free_var_type = opline -> op2_type ;
371
+ }
301
372
}
302
373
303
374
zend_ssa_rename_defs_of_instr (ctx -> ssa , ssa_op );
304
375
zend_ssa_remove_instr (ctx -> ssa , opline , ssa_op );
305
376
306
377
if (free_var >= 0 ) {
307
- // TODO Sometimes we can mark the var as EXT_UNUSED
308
378
opline -> opcode = ZEND_FREE ;
309
379
opline -> op1 .var = (uintptr_t ) ZEND_CALL_VAR_NUM (NULL , ssa -> vars [free_var ].var );
310
380
opline -> op1_type = free_var_type ;
0 commit comments