U
    ?h{                     @   s(  d Z ddlmZmZ ddlZddlZddlmZmZ ddl	m
Z
mZmZ ddlmZmZ ddlmZ eeZdd	 Zd
d ZeddZdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Z d$d% Z!d&d' Z"d(d) Z#d*d+ Z$d,d- Z%e&d.d/d0Z'dd1d2d3Z(dS )4z&
Implement transformation on Numba IR
    )
namedtupledefaultdictN)compute_cfg_from_blocksfind_top_level_loops)errorsirir_utils)compute_use_defsr   )	PYVERSIONc                    s   fdd}dd } fdd}t d g }tD ]L}t d| ||r6||r6||r6 |jkr6|| t d	| q6|S )
zE
    Returns a list of loops that are candidate for loop lifting
    c                    sd   t  }| jD ]8}t dd  |D }|s<td  dS ||O }qt|dk}td|| |S )z)all exits must point to the same locationc                 s   s   | ]\}}|V  qd S N ).0x_r   r   G/var/www/html/venv/lib/python3.8/site-packages/numba/core/transforms.py	<genexpr>   s     zL_extract_loop_lifting_candidates.<locals>.same_exit_point.<locals>.<genexpr>zreturn-statement in loop.F   zsame_exit_point=%s (%s))setexits
successors_loggerdebuglen)loopZoutedgesksuccsok)cfgr   r   same_exit_point   s    


z9_extract_loop_lifting_candidates.<locals>.same_exit_pointc                 S   s   t | jdk}td| |S )zthere is one entryr   zone_entry=%s)r   entriesr   r   )r   r   r   r   r   	one_entry(   s    z3_extract_loop_lifting_candidates.<locals>.one_entryc                    sv   t | jt | jB t | jB }t j|D ]<}|jD ]0}t|tjr4t|j	tj
r4td   dS q4q*td dS )z!cannot have yield inside the loopz	has yieldFzno yieldT)r   bodyr   r   map__getitem__
isinstancer   AssignvalueYieldr   r   )r   Zinsidersblkinstblocksr   r   cannot_yield.   s    


z6_extract_loop_lifting_candidates.<locals>.cannot_yieldzfinding looplift candidatesztop-level loop: %szadd candidate: %s)r   infor   r   entry_pointr   append)r   r+   r   r    r,   
candidatesr   r   )r+   r   r    _extract_loop_lifting_candidates   s    

r1   c                 C   s   || }|| }i }|D ]}| | ||< qt  }	t  }
t|}|j D ]}|	|O }	qH|j D ]}|
|O }
q`|	|
B }tt ||@ }tt ||@ |
@ }||fS )z7Find input and output variables to a block region.
    )r   r	   ZusemapvaluesZdefmapsorted)r+   livemapcallfromreturntobody_block_idsinputsoutputs
loopblocksr   Z	used_varsZdef_varsZdefsvsZused_or_definedr   r   r   find_region_inout_varsH   s     

r<   Zloop_lift_infoz%loop,inputs,outputs,callfrom,returntoc                 C   s   t | |}g }|D ]}|j\}tt|j}t|jdkrL| |\\}}	n|}t|jt|jB t|jB }
t	|||||
d\}}t
|||||d}|| q|S )z8
    Returns information on looplifting candidates.
    r   )r+   r4   r5   r6   r7   )r   r8   r9   r5   r6   )r1   r   nextiterr   r   r   r   r!   r<   _loop_lift_infor/   )r   r+   r4   loops	loopinfosr   r5   Zan_exitr6   r   Zlocal_block_idsr8   r9   Zllir   r   r   _loop_lift_get_candidate_infosh   s,    

 rB   c                 C   s2   |j }|j}tj||d}tj|| |||d |S )zR
    Transform calling block from top-level function to call the lifted loop.
    scopeloc)ZnewblockZcallee
label_nextr8   r9   )rD   rE   r   Blockr   Zfill_block_with_call)
liftedloopblockr8   r9   r6   rD   rE   r(   r   r   r   _loop_lift_modify_call_block   s    rJ   c                 C   sh   || j  }|j}|j}t|d }tjtj||d| j| j d||< tj	tj||d| j
d|| j< dS )z?
    Inplace transform loop blocks for use as lifted loop.
    r   rC   )rI   r8   rF   )rI   r9   N)r5   rD   rE   minr   Zfill_callee_prologuer   rG   r8   Zfill_callee_epiloguer9   r6   )loopinfor+   Zentry_blockrD   rE   Zfirstblkr   r   r   _loop_lift_prepare_loop_func   s    

rM   c                    s   ddl m} |j}t|jt|jB }	t|jdkr>|	|jO }	t fdd|	D }
t	||
 | j
|
t|jt|jdd}||||||}t| |j |j|j|j}|	D ]
} |= q| |j< |S )zu
    Modify the block inplace to call to the lifted-loop.
    Returns a dictionary of blocks of the lifted-loop.
    r   )
LiftedLoopr   c                 3   s   | ]}| |   fV  qd S r   )copy)r   r   r*   r   r   r      s     z+_loop_lift_modify_blocks.<locals>.<genexpr>T)r+   	arg_names	arg_countZforce_non_generator)numba.core.dispatcherrN   r   r   r!   r   r   r   dictrM   derivetupler8   rJ   r5   r9   r6   )func_irrL   r+   	typingctx	targetctxflagslocalsrN   r   Zloopblockkeysr:   Z	lifted_irrH   Z	callblockr   r   r*   r   _loop_lift_modify_blocks   s6    

    
r[   c                 C   sp   t |jdkrdS t|j}|  }t }t|}|rd| }|| ||| |h 8 }|| }q2t |dkS )zReturns True if there is more than one exit in the loop.

    NOTE: "common exits" refers to the situation where a loop exit has another
    loop exit as its successor. In that case, we do not need to alter it.
    r   F)r   r   r   Zpost_dominatorspopadd)r   Zlpinfor   Zpdom	processedremainnoder   r   r   _has_multiple_loop_exits   s    


ra   c                 C   sZ   ddl m} t| j}|  D ]}t||r"t| |j\} }q"| 	  || 
  | S )z(Canonicalize loops for looplifting.
    r   )PostProcessor)Znumba.core.postprocrb   r   r+   r@   r2   ra   _fix_multi_exit_blocksr   Z_reset_analysis_variablesrun)rV   rb   r   Z	loop_infoZ_common_keyr   r   r   _pre_looplift_transform   s    

 
re   c              	   C   s   t | } | j }t|}t||| jj}g }|rHtdt	|| 
  |D ]"}	t| |	|||||}
||
 qL| j|d}||fS )z
    Loop lifting transformation.

    Given a interpreter `func_ir` returns a 2 tuple of
    `(toplevel_interp, [loop0_interp, loop1_interp, ....])`
    z+loop lifting this IR with %d candidates:
%sr*   )re   r+   rO   r   rB   variable_lifetimer4   r   r   r   Zdump_to_stringr[   r/   rT   )rV   rW   rX   rY   rZ   r+   r   rA   r@   rL   Zliftedmainr   r   r   loop_lifting   s,    
    rh   c                    sf   t    fdd fddfdd}dd fd	d
}| D ]}|| qTS )z5
    Rewrite loops that have multiple backedges.
    c                      s   t   d S )Nr   )maxkeysr   )	newblocksr   r   new_block_id"  s    z6canonicalize_cfg_single_backedge.<locals>.new_block_idc                    sF   d}| j D ]6} | }|j }| j|kr
|d7 }|dkr
 dS q
dS )Nr   r   TF)r!   
terminatorget_targetsheader)r   countr   r(   edgesr*   r   r   has_multiple_backedges%  s    


z@canonicalize_cfg_single_backedge.<locals>.has_multiple_backedgesc                  3   s$       D ]} | r| V  qd S r   )r@   r2   )Zlp)r   rr   r   r   #yield_loops_with_multiple_backedges2  s    zMcanonicalize_cfg_single_backedge.<locals>.yield_loops_with_multiple_backedgesc                    sr    fdd}t | tjr<tj| j|| j|| j| jdS t | tjr^tj|| j| jdS | 	 rjt
| S d S )Nc                    s   | kr S | S r   r   )targetdstsrcr   r   replace8  s    zIcanonicalize_cfg_single_backedge.<locals>.replace_target.<locals>.replace)condtruebrfalsebrrE   rt   rE   )r$   r   Branchry   rz   r{   rE   Jumprt   rn   AssertionError)termrw   rv   rx   r   ru   r   replace_target7  s    z8canonicalize_cfg_single_backedge.<locals>.replace_targetc                    s   | j }  }| jD ]>}| }||j kr| }|j|||jd< ||< q| }tj|j|jd}|	tj
||jd ||< dS )zC
        Add new tail block that gathers all the backedges
        rC   r|   N)ro   r!   rm   rn   rO   r   rG   rD   rE   r/   r~   )r   ro   ZtailkeyZblkkeyr(   ZnewblkZentryblkZtailblk)rl   rk   r   r   r   rewrite_single_backedgeF  s    


zAcanonicalize_cfg_single_backedge.<locals>.rewrite_single_backedge)r   rO   )r+   rs   r   r   r   )r+   r   rr   rl   rk   r   r    canonicalize_cfg_single_backedge  s    

r   c                 C   s   t | S )zc
    Rewrite the given blocks to canonicalize the CFG.
    Returns a new dictionary of blocks.
    )r   r*   r   r   r   canonicalize_cfga  s    r   c              
      s   ddl m} d fdd	}t| \}} |s8| g fS ||   | jsPt| j}| j }	|j	}
g }|D ]d\}}g }t
|
||D ]}|| qt|	|  t| |	|\}}|| |	|||||}|| qn|s| }n
| |	}||fS )zWith-lifting transformation

    Rewrite the IR to extract all withs.
    Only the top-level withs are extracted.
    Returns the (the_new_ir, the_lifted_with_ir)
    r   )postprocFc                    sR   ddl m}m}   }|r:d|_d|_d|_d|_|}n|}|| |f|S )Nr   )
LiftedWithObjModeLiftedWithFT)rR   r   r   rO   Zenable_loopliftZenable_pyobjectZforce_pyobjectZno_cpython_wrapper)rV   Z
objectmodekwargsr   r   ZmyflagsclsrY   rZ   rX   rW   r   r   dispatcher_factoryr  s    z(with_lifting.<locals>.dispatcher_factory)F)
numba.corer   find_setupwithsrb   rd   rf   r   r+   rO   r   _cfg_nodes_in_regionr/   _legalize_with_head_get_with_contextmanagermutate_with_bodyrT   )rV   rW   rX   rY   rZ   r   r   withsZvltr+   r   Zsub_irs	blk_startZblk_endZbody_blocksr`   ZcmkindextrasubZnew_irr   r   r   with_liftingi  s6    

 
r   c                    s   d fdd fdd} j D ]L}t|tjr.|j}||\}}t|dsntjd jd||f  S q.tjd	 jdd
S )z7Get the global object used for the context manager
    zIllegal use of context-manager.c                    s
     | S )z#Get the definition given a variable)get_definition)varrV   r   r   get_var_dfn  s    z-_get_with_contextmanager.<locals>.get_var_dfnc                    s    | }t|tjrZ|jdkrZfdd|jD }fdd|jD }||d}|j} nd}t	tj
| }|tjkrtjd jd	|dkrtj |jd	||fS )
zReturn the context-manager object and extra info.

        The extra contains the arguments if the context-manager is used
        as a call.
        callc                    s   g | ]} |qS r   r   )r   r   r   r   r   
<listcomp>  s     zD_get_with_contextmanager.<locals>.get_ctxmgr_obj.<locals>.<listcomp>c                    s   i | ]\}}| |qS r   r   )r   r   vr   r   r   
<dictcomp>  s      zD_get_with_contextmanager.<locals>.get_ctxmgr_obj.<locals>.<dictcomp>)argsr   Nz*Undefined variable used as context managerrE   )r   r$   r   Expropr   kwsfuncr   guardZfind_outer_valueZ	UNDEFINEDr   CompilerErrorrE   )var_refdfnr   r   r   ctxobjZ_illegal_cm_msgr   r+   rV   r   r   r   get_ctxmgr_obj  s     


z0_get_with_contextmanager.<locals>.get_ctxmgr_objr   z"Unsupported context manager in user   zmalformed with-context usageN)	r!   r$   r   	EnterWithcontextmanagerhasattrr   r   rE   )rV   r+   r   r   stmtr   r   r   r   r   r   r     s"    
r   c                 C   s   t t}| jD ]}|t|  d7  < q|tjdkrHtjd| j	d|tj
ddkrjtjd| j	d|tjd |rtjd| j	ddS )zaGiven *blk*, the head block of the with-context, check that it doesn't
    do anything else.
    r   z0with's head-block must have exactly 1 ENTER_WITHr   r   z*with's head-block must have exactly 1 JUMPNz'illegal statements in with's head-block)r   intr!   typer\   r   r   r   r   rE   r~   Del)r(   Zcountersr   r   r   r   r     s&    
r   c           	         sf   t  |g}|rb| }t| |}|rt| \}}t  fdd|D }|| |O qS )z;Find the set of CFG nodes that are in the given region
    c                    s    g | ]}|kr| kr|qS r   r   )r   r`   
region_endZregion_nodesr   r   r     s    z(_cfg_nodes_in_region.<locals>.<listcomp>)r   r\   listr   zipextend)	r   Zregion_beginr   stackZtosZsucclistr   r   Znodesr   r   r   r     s    

r   c           	         s   dd } j }||}t||  dd | D }|D ],\}}|| j }t|dkr8tdq8|D ]H\}}|| }t	 j |j d  jrjt
dkrtd	t | qj fd
d|D }t|}| fS )zQFind all top-level with.

    Returns a list of ranges for the with-regions.
    c                 S   s(  t | }t t  }}|  D ]<\}}|jD ],}t|rD|| t|r,|| q,qtt}|j	|ddD ]}g g  }	}
|	
| |	rr|	 }|

| | | jD ]t}t|rtdt|r||kr|| | ||  qt|r| D ]}||
kr|	
| qqqqr|S )NT)reversezBunsupported control flow due to raise statements inside with block)r   r   itemsr!   r   Zis_setup_withr]   is_pop_blockr   Z	topo_sortr/   r\   Zis_raiser   r   removeZis_terminatorrn   )r+   r   Z
sus_setupsZsus_popslabelrI   r   Zsetup_with_to_pop_blocks_mapZsetup_blockZto_visitseentr   r   r   find_ranges  s:    










z$find_setupwiths.<locals>.find_rangesc                 S   s    g | ]\}}|t |d  fqS r   )r   r   spr   r   r   r   M  s   z#find_setupwiths.<locals>.<listcomp>r   zlunsupported control flow: with-context contains branches (i.e. break/return/raise) that can leave the block r   )      zDunsupported control flow: due to return statements inside with blockc                    s(   g | ] \}}| j | j d  fqS r   )r+   rm   rn   r   r   r   r   r   h  s   )r+   consolidate_multi_exit_withsr   rm   rn   r   r   r   r   Z	is_returnr
   _rewrite_return_eliminate_nested_withs)	rV   r   r+   Zwith_ranges_dictZwith_ranges_tupler   r   targetstarget_blockr   r   r   r     s8    2
r   c                 C   st  | j | }|j d }| j | }t| j }|d }|j}tjd|d}tj||d}	g g  }
}|	tj
}t|dkstt|	tjdkstt|jd tjst|d }|j|}|
|jd|  |
t||j ||j|d  |t||j | j | j}|	j| |j  |j| |j  |j|
 |	| j |< t| j | _| S )u:  Rewrite a return block inside a with statement.

    Arguments
    ---------

    func_ir: Function IR
      the CFG to transform
    target_block_label: int
      the block index/label of the block containing the POP_BLOCK statement


    This implements a CFG transformation to insert a block between two other
    blocks.

    The input situation is:

    ┌───────────────┐
    │   top         │
    │   POP_BLOCK   │
    │   bottom      │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │    RETURN     │
    │               │
    └───────────────┘

    If such a pattern is detected in IR, it means there is a `return` statement
    within a `with` context. The basic idea is to rewrite the CFG as follows:

    ┌───────────────┐
    │   top         │
    │   POP_BLOCK   │
    │               │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │     bottom    │
    │               │
    └───────┬───────┘
            │
    ┌───────▼───────┐
    │               │
    │    RETURN     │
    │               │
    └───────────────┘

    We split the block that contains the `POP_BLOCK` statement into two blocks.
    Everything from the beginning of the block up to and including the
    `POP_BLOCK` statement is considered the 'top' and everything below is
    considered 'bottom'. Finally the jump statements are re-wired to make sure
    the CFG remains valid.

    r   r   Nr   r   )r+   rm   rn   r   Zfind_max_labelrE   r   ZScoperG   Z
find_instsZPopBlockr   r   r~   r$   r!   indexr   r/   clearZbuild_definitionsZ_definitions)rV   Ztarget_block_labelr   Ztarget_block_successor_labelZtarget_block_successor	max_labelZ	new_labelZnew_block_locZnew_block_scopeZ	new_blockZtop_bodyZbottom_bodyZ
pop_blocksZ	pb_markerZpb_isZreturn_bodyr   r   r   r   q  s8    :






r   c                 C   s<   g }dd }t | D ]"\}}||||s|||f q|S )Nc                 S   s(   |D ]\}}| |kr||k r dS qdS )NTFr   )startendknown_rangesabr   r   r   within_known_range  s    z3_eliminate_nested_withs.<locals>.within_known_range)r3   r/   )Zwith_rangesr   r   r   er   r   r   r     s    	r   )r   c                 C   s@   | D ]6}| | }t |dkrt||tjd\}}|h| |< q|S )zGModify the FunctionIR to merge the exit blocks of with constructs.
    r   split_condition)r   rc   r   r   )r   r+   rV   r   r;   commonr   r   r   r     s      
r   r   c             
   C   s  | j }t| j  }|j}t| j d }tj|jtjd}|}|d7 }|||< tj|jtjd}	|}
|d7 }|	||
< g }t|D ]\}}|| }|dk	rt|j	D ]\}}||r qqnd}|j	d| }|j	|d }|
| ||_	|j}|j	
tjtj||d|jd|d|d |jr(t|j	
tj|tjd q|dk	r`|j	
|d d  |jrlt|j	
tj|
|d g }|D ]}|
| |d7 }q|	}tj}t|D ]\}}|jd|d}|jd	|d}|j	
tjtj||d||d |j	
tjtjjtj|d||d
||d |d  \}|j	
tj|||| |d tj||d}|||| < q|j	
tj||d | |fS )a   Modify the FunctionIR to create a single common exit node given the
    original exit nodes.

    Parameters
    ----------
    func_ir :
        The FunctionIR. Mutated inplace.
    exit_nodes :
        The original exit nodes. A sequence of block keys.
    split_condition : callable or None
        If not None, it is a callable with the signature
        `split_condition(statement)` that determines if the `statement` is the
        splitting point (e.g. `POP_BLOCK`) in an exit node.
        If it's None, the exit node is not split.
    r   r   Nr   z$cp)r&   rt   rE   r   z	$cp_checkz$cp_rhs)fnlhsrhsrE   rC   )r+   rK   r2   rD   ri   r   rG   Zunknown_loc	enumerater!   r/   rE   r%   ZConstZget_or_defineZis_terminatedr   r~   Zredefiner   Zbinopoperatoreqgetrn   r}   )rV   Z
exit_nodesr   r+   Zany_blkrD   r   Zcommon_blockZcommon_labelZ
post_blockZ
post_labelZ
remainingsir   r(   ptr   beforeafterrE   Zremain_blocksr_   Zswitch_blockZ
match_exprZ	match_rhsZjump_targetr   r   r   rc     s    4


	  rc   ))__doc__collectionsr   r   loggingr   Znumba.core.analysisr   r   r   r   r   r   r	   Znumba.core.utilsr
   	getLogger__name__r   r1   r<   r?   rB   rJ   rM   r[   ra   re   rh   r   r   r   r   r   r   r   r   r   rS   r   rc   r   r   r   r   <module>   s>   
5!'F;9ek