o
    ۾i|                     @   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%d.e&f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 ]&}t d| ||rA||rA||rA |jvrA|| t d	| q|S )
zE
    Returns a list of loops that are candidate for loop lifting
    c                    sd   t  }| jD ]}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   I/home/ubuntu/.local/lib/python3.10/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)loop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 ]}t|tjr2t|j	tj
r2td   dS qq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   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   

r3   c                 C   s   || }|| }i }|D ]}| | ||< qt  }	t  }
t|}|j D ]}|	|O }	q$|j D ]}|
|O }
q0|	|
B }tt ||@ }tt ||@ |
@ }||fS )z7Find input and output variables to a block region.
    )r   r	   usemapvaluesdefmapsorted)r-   livemapcallfromreturntobody_block_idsinputsoutputs
loopblocksr   	used_varsdef_varsdefsvsused_or_definedr   r   r   find_region_inout_varsH   s    

rD   loop_lift_infoz%loop,inputs,outputs,callfrom,returntoc                 C   s   t | |}g }|D ]G}|j\}tt|j}t|jdkr&| |\\}}	n|}t|jt|jB t|jB }
t	|||||
d\}}t
|||||d}|| q	|S )z8
    Returns information on looplifting candidates.
    r   )r-   r8   r9   r:   r;   )r   r<   r=   r9   r:   )r3   r    nextiterr   r   r   r   r"   rD   _loop_lift_infor1   )r   r-   r8   loops	loopinfosr   r9   an_exitr:   r   local_block_idsr<   r=   llir   r   r   _loop_lift_get_candidate_infosh   s*   

rN   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)newblockcallee
label_nextr<   r=   )rP   rQ   r   Blockr   fill_block_with_call)
liftedloopblockr<   r=   r:   rP   rQ   r*   r   r   r   _loop_lift_modify_call_block   s   rY   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   rO   )rX   r<   rT   )rX   r=   N)r9   rP   rQ   minr   fill_callee_prologuer   rU   r<   fill_callee_epiloguer=   r:   )loopinfor-   entry_blockrP   rQ   firstblkr   r   r   _loop_lift_prepare_loop_func   s   

r`   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	||
 g }|

 D ]}||jdd q5t|dd	 d
}|j}| j|
t|jt|jd|d}||||||}t| |j |j|j|j}|	D ]} |= qt| |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>getiter)opc                 S   s   | j jS r   )rQ   line)r   r   r   r   <lambda>   s    z*_loop_lift_modify_blocks.<locals>.<lambda>)keyT)r-   	arg_names	arg_countforce_non_generatorrQ   )numba.core.dispatcherra   r   r   r"   r    r   r   dictr`   r5   extend
find_exprsrZ   rQ   derivetupler<   rY   r9   r=   r:   )func_irr]   r-   	typingctx	targetctxflagslocalsra   r   loopblockkeysr>   getiter_exprsr*   first_getiterloop_loc	lifted_irrW   	callblockr   r   r,   r   _loop_lift_modify_blocks   s:   


r|   c                 C   sr   t |jdkr	dS t|j}|  }t }t|}|r3| }|| ||| |h 8 }|| }|st |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   post_dominatorspopadd)r   lpinfor   pdom	processedremainnoder   r   r   _has_multiple_loop_exits   s   

r   c                 C   sZ   ddl m} t| j}|  D ]}t||r t| |j\} }q| 	  || 
  | S )z(Canonicalize loops for looplifting.
    r   )PostProcessor)numba.core.postprocr   r   r-   rI   r5   r   _fix_multi_exit_blocksr   _reset_analysis_variablesrun)rq   r   r   	loop_info_common_keyr   r   r   _pre_looplift_transform   s   

r   c              	   C   s   t | } | j }t|}t||| jj}g }|r$tdt	|| 
  |D ]}	t| |	|||||}
||
 q&| 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,   )r   r-   rb   r   rN   variable_lifetimer8   r   r   r   dump_to_stringr|   r1   ro   )rq   rr   rs   rt   ru   r-   r   rJ   rI   r]   liftedmainr   r   r   loop_lifting  s$   
r   c                    sf   t    fdd fddfdd}dd fd	d
}| D ]}|| q*S )z5
    Rewrite loops that have multiple backedges.
    c                      s   t   d S )Nr   )maxkeysr   )	newblocksr   r   new_block_id*     z6canonicalize_cfg_single_backedge.<locals>.new_block_idc                    sF   d}| j D ]} | }|j }| j|v 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   )rI   r5   )lp)r   r   r   r   #yield_loops_with_multiple_backedges:  s   zMcanonicalize_cfg_single_backedge.<locals>.yield_loops_with_multiple_backedgesc                    sn    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 | 	 r5J | S )Nc                    s   | kr S | S r   r   )targetdstsrcr   r   replace@  r   zIcanonicalize_cfg_single_backedge.<locals>.replace_target.<locals>.replace)condtruebrfalsebrrQ   r   rQ   )
r%   r   Branchr   r   r   rQ   Jumpr   r   )termr   r   r   r   r   r   replace_target?  s   z8canonicalize_cfg_single_backedge.<locals>.replace_targetc                    s   | j }  }| jD ]}| }||j v 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
        rO   r   N)r   r"   r   r   rb   r   rU   rP   rQ   r1   r   )r   r   tailkeyblkkeyr*   newblkentryblktailblk)r   r   r   r   r   rewrite_single_backedgeN  s   

zAcanonicalize_cfg_single_backedge.<locals>.rewrite_single_backedge)r   rb   )r-   r   r   r   r   )r-   r   r   r   r   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_cfgi  s   r   c              
      s   ddl m} d fdd	}t| \}} |s| g fS ||   | js(J | j}| j }	|j}
g }|D ]2\}}g }t	|
||D ]}|
| qCt|	|  t| |	|\}}|| |	|||||}|
| q7|sr| }||fS | |	}||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                    sV   ddl m}m}   }|rd|_d|_d|_d|_|}n|}|| |fi |S )Nr   )
LiftedWithObjModeLiftedWithFT)rk   r   r   rb   enable_loopliftenable_pyobjectforce_pyobjectno_cpython_wrapper)rq   
objectmodekwargsr   r   myflagsclsrt   ru   rs   rr   r   r   dispatcher_factoryz  s   z(with_lifting.<locals>.dispatcher_factoryN)F)
numba.corer   find_setupwithsr   r   r   r-   rb   r   _cfg_nodes_in_regionr1   _legalize_with_head_get_with_contextmanagermutate_with_bodyro   )rq   rr   rs   rt   ru   r   r   withsvltr-   r   sub_irs	blk_startblk_endbody_blocksr   cmkindextrasubnew_irr   r   r   with_liftingq  s6   


r   c                    s   d fdd fdd} j D ]&}t|tjr=|j}||\}}t|ds7tjd jd||f  S qtjd	 jd)
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rq   r   r   get_var_dfn  s   
z-_get_with_contextmanager.<locals>.get_var_dfnc                    s    | }t|tjr-|jdkr-fdd|jD }fdd|jD }||d}|j} nd}t	tj
| }|tju rFtjd jd	|du rRt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rQ   )r   r%   r   Exprrd   r   kwsfuncr   guardfind_outer_value	UNDEFINEDr   CompilerErrorrQ   )var_refdfnr   r   r   ctxobj_illegal_cm_msgr   r-   rq   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 usage)	r"   r%   r   	EnterWithcontextmanagerhasattrr   r   rQ   )rq   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r$tjd| j	d|tj
ddkr5tjd| j	d|tjd |rFt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   rQ   r   Del)r*   countersr   r   r   r   r     s(   
r   c           	         sh   t  |g}|r2| }t| |}|r0t| \}}t  fdd|D }|| |O |sS )z;Find the set of CFG nodes that are in the given region
    c                    s    g | ]}|vr| kr|qS r   r   )r   r   
region_endregion_nodesr   r   r     s
    z(_cfg_nodes_in_region.<locals>.<listcomp>)r   r~   listr   ziprm   )	r   region_beginr   stacktossucclistr   r   nodesr   r   r   r      s   
r   c           	         s   dd } j }||}t||  dd | D }|D ]\}}|| j }t|dkr2tdq|D ]\}}|| }t	 j |j d  jrPt
 | q5 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|r"|| t|r,|| qqtt}|j	|ddD ]W}g g }	}
|	
| |	r|	 }|

| | | jD ]8}t|ratdt|rx||v rx|| | ||  nt|r| D ]}||
vr|	
| qqU|	sGq9|S )NT)reversezBunsupported control flow due to raise statements inside with block)r   r   itemsr"   r   is_setup_withr   is_pop_blockr   	topo_sortr1   r~   is_raiser   r   removeis_terminatorr   )r-   r   
sus_setupssus_popslabelrX   r   setup_with_to_pop_blocks_mapsetup_blockto_visitseentr   r   r   find_ranges  sD   











z$find_setupwiths.<locals>.find_rangesc                 S   s    g | ]\}}|t |d  fqS r   )r   r   spr   r   r   r   U  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   c                    s(   g | ]\}}| j | j d  fqS r  )r-   r   r   r  r   r   r   r   i  s    )r-   consolidate_multi_exit_withsr   r   r   r   r   r   r   	is_return_rewrite_return_eliminate_nested_withs)	rq   r  r-   with_ranges_dictwith_ranges_tupler   r  targetstarget_blockr   r   r   r     s8   2

r   c                 C   sx  | j | }|j d }| j | }t| j }|d }|j}tjd|d}tj||d}	g g }
}g |	tj
}t|dksAJ tg |	tjdksOJ t|jd tjsZJ |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-   r   r   r   find_max_labelrQ   r   ScoperU   
find_instsPopBlockr   r   r%   r"   indexrm   r1   clearbuild_definitions_definitions)rq   target_block_labelr  target_block_successor_labeltarget_block_successor	max_label	new_labelnew_block_locnew_block_scope	new_blocktop_bodybottom_body
pop_blocks	pb_markerpb_isreturn_bodyr   r   r   r  r  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)r7   r1   )with_rangesr3  r6  r  er   r   r   r    s   	r  r   c                 C   s@   | D ]}| | }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   r   r   r  )r   r-   rq   r   rB   commonr   r   r   r    s   

r  r9  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ur]t|j	D ]
\}}||r[ nqQnd}|j	d| }|j	|d }|
| ||_	|j}|j	
tjtj||d|jd|d|d |jrJ |j	
tj|tjd q@|dur|j	
|d d  |jrJ |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'   r   rQ   r   z	$cp_checkz$cp_rhs)fnlhsrhsrQ   rO   )r-   rZ   r5   rP   r   r   rU   unknown_loc	enumerater"   r1   rQ   r&   Constget_or_defineis_terminatedr   redefiner   binopoperatoreqgetr   r   )rq   
exit_nodesr:  r-   any_blkrP   r&  common_blockcommon_label
post_block
post_label
remainingsir   r*   ptr   beforeafterrQ   remain_blocksr   switch_block
match_expr	match_rhsjump_targetr   r   r   r     s   4




	r   ))__doc__collectionsr   r   loggingrF  numba.core.analysisr   r   r   r   r   r   r	   numba.core.utilsr
   	getLogger__name__r   r3   rD   rH   rN   rY   r`   r|   r   r   r   r   r   r   r   r   r   r   r  r  rl   r  r   r   r   r   r   <module>   s@    
5!/F;9^k