[LLVMdev] Proposal: Loads/stores with deterministic trap/unwind behavior

Peter Collingbourne peter at pcc.me.uk
Mon Mar 31 18:58:03 PDT 2014


Hi,

I wanted to propose an IR extension that would allow us to support zero-cost
exception handling for non-call operations that may trap. I wanted to start
with loads and stores through a null pointer, and later we might extend this to
div/rem/mod zero. This feature is obviously useful for implementing languages
such as Java and Go which deterministically translate such operations into
exceptions which may be caught by the user.

There are a couple of somewhat orthogonal features that this would entail:

 1) Deterministic handling for loads and stores through a null pointer.
 2) Ability to unwind a load/store to a specific basic block, like invoke.

At the moment, we do not exactly have 1), as the optimizer considers
non-volatile loads/stores through a null pointer to have undefined
behavior. Volatile loads/stores are closer, but they come with their own
set of baggage that can inhibit optimization. (For example, if we can prove
that a load would always succeed, 'volatile' prevents us from reordering
the load or deleting it if it is dead.) So I propose to add an attribute to
'load' and 'store', which we can call, say, 'nullcheck', with the following
additional semantics:

 - If the memory address is between zero and a target-defined value (i.e. the
   size of the zero page) the instruction is guaranteed to trap in a
   target-defined manner.

 - The optimizer may only delete or reorder nullcheck instructions if the
   program cannot observe such a transformation by installing a signal handler
   for the trap.  Therefore, the optimizer would be able to drop the attribute
   if it can prove that the address will always be non-null.

To support 2), I propose a couple of new instructions. I haven't come up with
great names for these instructions, but:

 - 'iload' is to 'load' as 'invoke' is to 'call'. That is, the instruction is
   a terminator and has normal and unwind destinations. e.g.

   %v = iload i8* %ptr to label %try.cont unwind label %lpad

 - Similarly, 'istore' is to 'store' as 'invoke' is to 'call'.

   istore i8 %v, i8* %ptr to label %try.cont unwind label %lpad

These instructions always have 'nullcheck' semantics, plus:

 - If the instruction traps and the program has installed a signal handler
   for the trap which unwinds, the unwind is guaranteed to land at the
   landing pad.

I've been working on an implementation of 'iload' and 'istore' which are
in the attached patches, if you are interested. (They aren't ready to go
in yet.) I have asm parsing/printing for both, and code generation for
'iload'. Would be interested in getting feedback on code generation as this
is my first serious foray into the backend -- I haven't tried running the
generated code yet and the DAG builder is a mashup of the DAG builders for
'invoke' and 'load', but I've eyeballed the asm it generates (e.g. llc produces
iload-exception.s for the attached iload-exception.ll) and it looks reasonable.

Thanks,
-- 
Peter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Create-UnwindPoint-as-a-base-class-of-InvokeInst.patch
Type: text/x-diff
Size: 12876 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140331/913072b5/attachment.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-Add-ILoadInst-and-IStoreInst-classes.patch
Type: text/x-diff
Size: 29753 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140331/913072b5/attachment-0001.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0003-Add-SelectionDAG-support-for-iload.patch
Type: text/x-diff
Size: 4737 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140331/913072b5/attachment-0002.patch>
-------------- next part --------------
; RUN: llvm-as < %s | llvm-dis > %t1.ll
; RUN: llvm-as %t1.ll -o - | llvm-dis > %t2.ll
; RUN: diff %t1.ll %t2.ll

@_ZTIc = external constant i8*
@_ZTId = external constant i8*
@_ZTIPKc = external constant i8*

define i8 @_Z3barv(i8* %ptr) uwtable optsize ssp {
entry:
  %v = iload i8* %ptr
          to label %try.cont unwind label %lpad

try.cont:                                         ; preds = %entry, %invoke.cont4
  ret i8 %v

lpad:                                             ; preds = %entry
  %exn = landingpad {i8*, i32} personality i32 (...)* @__gxx_personality_v0
            cleanup
            catch i8** @_ZTIc
            filter [2 x i8**] [i8** @_ZTIPKc, i8** @_ZTId]
  resume { i8*, i32 } %exn
}

declare i32 @__gxx_personality_v0(...)
-------------- next part --------------
	.text
	.file	"../llvm/test/Feature/iload-exception.ll"
	.globl	_Z3barv
	.type	_Z3barv, at function
_Z3barv:                                # @_Z3barv
	.cfi_startproc
	.cfi_personality 3, __gxx_personality_v0
.Leh_func_begin0:
	.cfi_lsda 3, .Lexception0
# BB#0:                                 # %entry
	pushq	%rax
.Ltmp3:
	.cfi_def_cfa_offset 16
.Ltmp0:
	movb	(%rdi), %al
.Ltmp1:
# BB#1:                                 # %try.cont
	popq	%rdx
	retq
.LBB0_2:                                # %lpad
.Ltmp2:
	movq	%rax, %rdi
	callq	_Unwind_Resume
.Ltmp4:
	.size	_Z3barv, .Ltmp4-_Z3barv
	.cfi_endproc
.Leh_func_end0:
	.section	.gcc_except_table,"a", at progbits
	.align	4
GCC_except_table0:
.Lexception0:
	.byte	255                     # @LPStart Encoding = omit
	.byte	3                       # @TType Encoding = udata4
	.asciz	"\256\200\200"          # @TType base offset
	.byte	3                       # Call site Encoding = udata4
	.byte	26                      # Call site table length
.Lset0 = .Ltmp0-.Leh_func_begin0        # >> Call Site 1 <<
	.long	.Lset0
.Lset1 = .Ltmp1-.Ltmp0                  #   Call between .Ltmp0 and .Ltmp1
	.long	.Lset1
.Lset2 = .Ltmp2-.Leh_func_begin0        #     jumps to .Ltmp2
	.long	.Lset2
	.byte	5                       #   On action: 3
.Lset3 = .Ltmp1-.Leh_func_begin0        # >> Call Site 2 <<
	.long	.Lset3
.Lset4 = .Leh_func_end0-.Ltmp1          #   Call between .Ltmp1 and .Leh_func_end0
	.long	.Lset4
	.long	0                       #     has no landing pad
	.byte	0                       #   On action: cleanup
	.byte	0                       # >> Action Record 1 <<
                                        #   Cleanup
	.byte	0                       #   No further actions
	.byte	127                     # >> Action Record 2 <<
                                        #   Filter TypeInfo -1
	.byte	125                     #   Continue to action 1
	.byte	3                       # >> Action Record 3 <<
                                        #   Catch TypeInfo 3
	.byte	125                     #   Continue to action 2
                                        # >> Catch TypeInfos <<
	.long	_ZTIc                   # TypeInfo 3
	.long	_ZTId                   # TypeInfo 2
	.long	_ZTIPKc                 # TypeInfo 1
                                        # >> Filter TypeInfos <<
	.byte	1                       # FilterInfo -1
	.byte	2                       # FilterInfo -2
	.byte	0
	.align	4


	.section	".note.GNU-stack","", at progbits


More information about the llvm-dev mailing list