B
     \]@                 @   s   d Z ddlmZmZ ddlmZmZ ddlmZm	Z	 ddl
mZmZmZ ddlm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'S )(z&
Implement transformation on Numba IR
    )absolute_importprint_function)
namedtupledefaultdict)compute_cfg_from_blocksfind_top_level_loops)irerrorsir_utils)compute_use_defsc                s:   fdddd  fddfddt D S )	zE
    Returns a list of loops that are candidate for loop lifting
    c                sJ   t  }x6| jD ],}t dd  |D }|s2dS ||O }qW t|dkS )z)all exits must point to the same locationc             s   s   | ]\}}|V  qd S )N ).0x_r   r   /lib/python3.7/site-packages/numba/transforms.py	<genexpr>   s    zL_extract_loop_lifting_candidates.<locals>.same_exit_point.<locals>.<genexpr>F   )setexits
successorslen)loopZoutedgesksuccs)cfgr   r   same_exit_point   s    z9_extract_loop_lifting_candidates.<locals>.same_exit_pointc             S   s   t | jdkS )zthere is one entryr   )r   entries)r   r   r   r   	one_entry    s    z3_extract_loop_lifting_candidates.<locals>.one_entryc                sf   t | jt | jB t | jB }xBt j|D ]2}x,|jD ]"}t|tjr8t|j	tj
r8dS q8W q,W dS )z!cannot have yield inside the loopFT)r   bodyr   r   map__getitem__
isinstancer   ZAssignvalueZYield)r   ZinsidersblkZinst)blocksr   r   cannot_yield$   s    z6_extract_loop_lifting_candidates.<locals>.cannot_yieldc                s(   g | ] }|r|r |r|qS r   r   )r   r   )r%   r   r   r   r   
<listcomp>.   s    z4_extract_loop_lifting_candidates.<locals>.<listcomp>)r   )r   r$   r   )r$   r%   r   r   r   r    _extract_loop_lifting_candidates   s    
r'   c             C   s   || }|| }i }x|D ]}| | ||< qW t  }	t  }
t|}x|j D ]}|	|O }	qNW x|j D ]}|
|O }
qjW |	|
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defsZvsZused_or_definedr   r   r   find_region_inout_vars2   s     
r1   Zloop_lift_infoz%loop,inputs,outputs,callfrom,returntoc             C   s   t | |}g }x|D ]z}|j\}tt|j}| |\\}}	t|jt|jB t|jB }
t|||||
d\}}t	|||||d}|
| qW |S )z8
    Returns information on looplifting candidates.
    )r$   r*   r+   r,   r-   )r   r.   r/   r+   r,   )r'   r   nextiterr   r   r   r   r1   _loop_lift_infoappend)r   r$   r*   loops	loopinfosr   r+   Zan_exitr,   r   Zlocal_block_idsr.   r/   Zllir   r   r   _loop_lift_get_candidate_infosR   s"    


r8   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_nextr.   r/   )r9   r:   r   Blockr
   Zfill_block_with_call)
liftedloopblockr.   r/   r,   r9   r:   r#   r   r   r   _loop_lift_modify_call_blockm   s    r?   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   )r9   r:   )r>   r.   r;   )r>   r/   N)r+   r9   r:   minr
   Zfill_callee_prologuer   r<   r.   Zfill_callee_epiloguer/   r,   )loopinfor$   Zentry_blockr9   r:   Zfirstblkr   r   r   _loop_lift_prepare_loop_func   s    
rB   c                s   ddl m} |j}t|jt|jB t|jB }	t fdd|	D }
t||
 | j	|
t
|jt|jdd}||||||}t| |j |j|j|j}x|	D ]
} |= qW | |j< |S )zu
    Modify the block inplace to call to the lifted-loop.
    Returns a dictionary of blocks of the lifted-loop.
    r   )
LiftedLoopc             3   s   | ]}| |   fV  qd S )N)copy)r   r   )r$   r   r   r      s    z+_loop_lift_modify_blocks.<locals>.<genexpr>T)r$   Z	arg_namesZ	arg_countZforce_non_generator)numba.dispatcherrC   r   r   r   r   r   dictrB   derivetupler.   r   r?   r+   r/   r,   )func_irrA   r$   	typingctx	targetctxflagslocalsrC   r   Zloopblockkeysr0   Z	lifted_irr=   Z	callblockr   r   )r$   r   _loop_lift_modify_blocks   s$    



rN   c          	   C   sf   | j  }t|}t||| jj}g }x*|D ]"}	t| |	|||||}
||
 q,W | j|d}||fS )z
    Loop lifting transformation.

    Given a interpreter `func_ir` returns a 2 tuple of
    `(toplevel_interp, [loop0_interp, loop1_interp, ....])`
    )r$   )	r$   rD   r   r8   variable_lifetimer*   rN   r5   rG   )rI   rJ   rK   rL   rM   r$   r   r7   r6   rA   Zliftedmainr   r   r   loop_lifting   s    


rQ   c                sj   t    fdd fddfdd}dd fd	d
}x| D ]}|| qVW 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                sH   d}x>| j D ]4} | }|j }| j|kr|d7 }|dkrdS qW dS )Nr   r   TF)r   
terminatorget_targetsheader)r   countr   r#   Zedges)r$   r   r   has_multiple_backedges   s    

z@canonicalize_cfg_single_backedge.<locals>.has_multiple_backedgesc              3   s(   x"    D ]} | r| V  qW d S )N)r6   r(   )Zlp)r   rZ   r   r   #yield_loops_with_multiple_backedges   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 )Nr   )target)dstsrcr   r   replace   s    zIcanonicalize_cfg_single_backedge.<locals>.replace_target.<locals>.replace)condtruebrfalsebrr:   )r\   r:   )r!   r   ZBranchr`   ra   rb   r:   Jumpr\   rW   AssertionError)Ztermr^   r]   r_   r   )r]   r^   r   replace_target   s    
z8canonicalize_cfg_single_backedge.<locals>.replace_targetc                s   | j }  }xH| jD ]>}| }||j kr| }|j|||jd< ||< qW | }tj|j|jd}|	tj
||jd ||< dS )zC
        Add new tail block that gathers all the backedges
        )r9   r:   )r\   r:   N)rX   r   rV   rW   rD   r   r<   r9   r:   r5   rc   )r   rX   ZtailkeyZblkkeyr#   ZnewblkZentryblkZtailblk)rU   rT   re   r   r   rewrite_single_backedge   s    zAcanonicalize_cfg_single_backedge.<locals>.rewrite_single_backedge)r   rD   )r$   r[   rg   r   r   )r$   r   rZ   rU   rT   re   r    canonicalize_cfg_single_backedge   s    rh   c             C   s   t | S )zc
    Rewrite the given blocks to canonicalize the CFG.
    Returns a new dictionary of blocks.
    )rh   )r$   r   r   r   canonicalize_cfg  s    ri   c          
      s   ddl m} d fdd	}||   | js8t| j}| j }t|}	|j	}
t
|	|
| g }xp|	D ]h\}}g }xt|
||D ]}|| qW t||  t| ||\}}|| ||||||}|| qlW |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                sL   ddl m}m}   }|r4d|_d|_d|_|}n|}|| |f|S )Nr   )
LiftedWithObjModeLiftedWithFT)rE   rk   rl   rD   Zenable_loopliftZenable_pyobjectZforce_pyobject)rI   Z
objectmodekwargsrk   rl   Zmyflagscls)rL   rM   rK   rJ   r   r   dispatcher_factory%  s    z(with_lifting.<locals>.dispatcher_factory)F)numbarj   ZPostProcessorrunrO   rd   r$   rD   find_setupwithsr   _legalize_withs_cfg_cfg_nodes_in_regionr5   _legalize_with_head_get_with_contextmanagermutate_with_bodyrG   )rI   rJ   rK   rL   rM   rj   ro   Zvltr$   withsr   Zsub_irs	blk_startZblk_endZbody_blocksnodeZcmkindextrasubZnew_irr   )rL   rM   rK   rJ   r   with_lifting  s0    


r}   c                s   d fdd fdd}xV j D ]H}t|tjr0|j}||\}}t|dsptjd jd||fS q0W 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)rI   r   r   get_var_dfnY  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.
        Zcallc                s   g | ]} |qS r   r   )r   r   )r   r   r   r&   f  s    zD_get_with_contextmanager.<locals>.get_ctxmgr_obj.<locals>.<listcomp>c                s   i | ]\}} ||qS r   r   )r   r   v)r   r   r   
<dictcomp>g  s    zD_get_with_contextmanager.<locals>.get_ctxmgr_obj.<locals>.<dictcomp>)argsrm   Nz*Undefined variable used as context manager)r:   )r~   r!   r   ZExpropr   kwsfuncr
   ZguardZfind_global_valueZ	UNDEFINEDr	   CompilerErrorr:   )var_refZdfnr   r   r{   ctxobj)_illegal_cm_msgry   r$   rI   r   r   r   get_ctxmgr_obj]  s    


z0_get_with_contextmanager.<locals>.get_ctxmgr_objrw   z"Unsupported context manager in use)r:   zmalformed with-context usageN)	r   r!   r   	EnterWithcontextmanagerhasattrr	   r   r:   )rI   r$   ry   r   stmtr   r   r{   r   )r   ry   r$   rI   r   r   rv   T  s    
rv   c             C   s   t t}x"| jD ]}|t|  d7  < qW |tjdkrLtjd| j	d|tj
dkrlt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_WITH)r:   z*with's head-block must have exactly 1 JUMPNz'illegal statements in with's head-block)r   intr   typepopr   r   r	   r   r:   rc   ZDel)r#   Zcountersr   r   r   r   ru     s     

ru   c                s^   t  |g}xL|rX| }t| | \}}t  fdd|D }|| |O qW 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   rz   )
region_endregion_nodesr   r   r&     s    z(_cfg_nodes_in_region.<locals>.<listcomp>)r   r   zipr   extend)r   Zregion_beginr   stackZtosr   r   Znodesr   )r   r   r   rt     s    
rt   c       	      C   sl   |  }| }xV| D ]N\}}|| j}||| krFd}tj||d||| krd}tj||dqW dS )z+Verify the CFG of the with-context(s).
    z.Entry of with-context not dominating the exit.)r:   zDoes not support with-context that contain branches (i.e. break/return/raise) that can leave the with-context. Details: exit of with-context not post-dominating the entry. N)Z
dominatorsZpost_dominatorsr:   r	   r   )	rx   r   r$   ZdomsZpostdomsser:   msgr   r   r   rs     s    
rs   c                sp   dd } fdd}g }xRt || D ]B\ }| |s&|| krJtd | ksZtd| |f q&W |S )zQFind all top-level with.

    Returns a list of ranges for the with-regions.
    c             s   s:   x4|   D ](}x"|tjD ]}|j|jfV  qW q
W d S )N)r(   Z
find_instsr   r   Zbeginend)r$   r#   Zewr   r   r   find_ranges  s    z$find_setupwiths.<locals>.find_rangesc                s*   x$|D ]\}} |kr |k rdS qW dS )NTFr   )startknown_rangesab)r   r   r   previously_occurred  s    z,find_setupwiths.<locals>.previously_occurredzHunsupported controlflow due to return/raise statements inside with blockzstarting offset is not a label)r)   r	   r   rd   r5   )r$   r   r   r   r   r   )r   r   rr     s    
rr   N)__doc__Z
__future__r   r   collectionsr   r   Znumba.analysisr   r   rp   r   r	   r
   r   r'   r1   r4   r8   r?   rB   rN   rQ   rh   ri   r}   rv   ru   rt   rs   rr   r   r   r   r   <module>   s,   $#F89