How are references implemented internally?

Just to repeat some of the stuff everyone’s been saying, lets look at some compiler output:

#include <stdio.h>
#include <stdlib.h>

int byref(int & foo)
{
  printf("%d\n", foo);
}
int byptr(int * foo)
{
  printf("%d\n", *foo);
}

int main(int argc, char **argv) {
  int aFoo = 5; 
  byref(aFoo);
  byptr(&aFoo);
}

We can compile this with LLVM (with optimizations turned off) and we get the following:

define i32 @_Z5byrefRi(i32* %foo) {
entry:
  %foo_addr = alloca i32*                         ; <i32**> [#uses=2]
  %retval = alloca i32                            ; <i32*> [#uses=1]
  %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
  store i32* %foo, i32** %foo_addr
  %0 = load i32** %foo_addr, align 8              ; <i32*> [#uses=1]
  %1 = load i32* %0, align 4                      ; <i32> [#uses=1]
  %2 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %1) ; <i32> [#uses=0]
  br label %return

return:                                           ; preds = %entry
  %retval1 = load i32* %retval                    ; <i32> [#uses=1]
  ret i32 %retval1
}

define i32 @_Z5byptrPi(i32* %foo) {
entry:
  %foo_addr = alloca i32*                         ; <i32**> [#uses=2]
  %retval = alloca i32                            ; <i32*> [#uses=1]
  %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
  store i32* %foo, i32** %foo_addr
  %0 = load i32** %foo_addr, align 8              ; <i32*> [#uses=1]
  %1 = load i32* %0, align 4                      ; <i32> [#uses=1]
  %2 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %1) ; <i32> [#uses=0]
  br label %return

return:                                           ; preds = %entry
  %retval1 = load i32* %retval                    ; <i32> [#uses=1]
  ret i32 %retval1
}

The bodies of both functions are identical

Leave a Comment