char *string(const char *str) {
  static struct string {
    struct string *link;
    char str[1];
  } * list;
  struct string *p;
  for (p = list; p; p = p->link)
    if (strcmp(p->str, str) == 0)
      return p->str;
  p = alloc(strlen(str) + sizeof *p);
  strcpy(p->str, str);
  p->link = list;
  list = p;
  return p->str;
}


x86 assembly (clang, -O0):
<string>:
push   %rbp
mov    %rsp,%rbp
sub    $0x20,%rsp
mov    %rdi,-0x10(%rbp)
mov    0x2ef5(%rip),%rax
mov    %rax,-0x18(%rbp)
cmpq   $0x0,-0x18(%rbp)
je     11a2 <string+0x62>
mov    -0x18(%rbp),%rdi
add    $0x8,%rdi
mov    -0x10(%rbp),%rsi
call   1050 <strcmp@plt>
cmp    $0x0,%eax
jne    118d <string+0x4d>
mov    -0x18(%rbp),%rax
add    $0x8,%rax
mov    %rax,-0x8(%rbp)
jmp    11f5 <string+0xb5>
jmp    1192 <string+0x52>
mov    -0x18(%rbp),%rax
mov    (%rax),%rax
mov    %rax,-0x18(%rbp)
jmp    1157 <string+0x17>
mov    -0x10(%rbp),%rdi
call   1040 <strlen@plt>
mov    %rax,%rdi
add    $0x10,%rdi
mov    $0x0,%al
call   1060 <alloc@plt>
cltq
mov    %rax,-0x18(%rbp)
mov    -0x18(%rbp),%rdi
add    $0x8,%rdi
mov    -0x10(%rbp),%rsi
call   1030 <strcpy@plt>
mov    0x2e71(%rip),%rcx
mov    -0x18(%rbp),%rax
mov    %rcx,(%rax)
mov    -0x18(%rbp),%rax
mov    %rax,0x2e5f(%rip)
mov    -0x18(%rbp),%rax
add    $0x8,%rax
mov    %rax,-0x8(%rbp)
mov    -0x8(%rbp),%rax
add    $0x20,%rsp
pop    %rbp
ret

x86 assembly (clang, -O2):
<string>:
push   %r15
push   %r14
push   %rbx
mov    %rdi,%r14
mov    0x2ef9(%rip),%rbx
test   %rbx,%rbx
je     117b <string+0x3b>
cs nopw 0x0(%rax,%rax,1)
xchg   %ax,%ax
lea    0x8(%rbx),%r15
mov    %r15,%rdi
mov    %r14,%rsi
call   1050 <strcmp@plt>
test   %eax,%eax
je     11b1 <string+0x71>
mov    (%rbx),%rbx
test   %rbx,%rbx
jne    1160 <string+0x20>
mov    %r14,%rdi
call   1040 <strlen@plt>
lea    0x10(%rax),%rdi
xor    %eax,%eax
call   1060 <alloc@plt>
movslq %eax,%rbx
lea    0x8(%rbx),%r15
mov    %r15,%rdi
mov    %r14,%rsi
call   1030 <strcpy@plt>
mov    0x2ea1(%rip),%rax
mov    %rax,(%rbx)
mov    %rbx,0x2e97(%rip)
mov    %r15,%rax
pop    %rbx
pop    %r14
pop    %r15
ret

LLVM IR (-O0):
%struct.string = type { %struct.string*, [1 x i8] }
@string.list = internal global %struct.string* null
define i8* @string(i8* %0) {
%2 = alloca i8*
%3 = alloca i8*
%4 = alloca %struct.string*
store i8* %0, i8** %3
%5 = load %struct.string*, %struct.string** @string.list
store %struct.string* %5, %struct.string** %4
br label %6
6: ; preds = %21, %1
%7 = load %struct.string*, %struct.string** %4
%8 = icmp ne %struct.string* %7, null
br i1 %8, label %9, label %25
9: ; preds = %6
%10 = load %struct.string*, %struct.string** %4
%11 = getelementptr inbounds %struct.string, %struct.string* %10, i32 0, i32 1
%12 = getelementptr inbounds [1 x i8], [1 x i8]* %11, i64 0, i64 0
%13 = load i8*, i8** %3
%14 = call i32 @strcmp(i8* %12, i8* %13)
%15 = icmp eq i32 %14, 0
br i1 %15, label %16, label %20
16: ; preds = %9
%17 = load %struct.string*, %struct.string** %4
%18 = getelementptr inbounds %struct.string, %struct.string* %17, i32 0, i32 1
%19 = getelementptr inbounds [1 x i8], [1 x i8]* %18, i64 0, i64 0
store i8* %19, i8** %2
br label %44
20: ; preds = %9
br label %21
21: ; preds = %20
%22 = load %struct.string*, %struct.string** %4
%23 = getelementptr inbounds %struct.string, %struct.string* %22, i32 0, i32 0
%24 = load %struct.string*, %struct.string** %23
store %struct.string* %24, %struct.string** %4
br label %6
25: ; preds = %6
%26 = load i8*, i8** %3
%27 = call i64 @strlen(i8* %26)
%28 = add i64 %27, 16
%29 = call i32 (i64, ...) bitcast (i32 (...)* @alloc to i32 (i64, ...)*)(i64 %28)
%30 = sext i32 %29 to i64
%31 = inttoptr i64 %30 to %struct.string*
store %struct.string* %31, %struct.string** %4
%32 = load %struct.string*, %struct.string** %4
%33 = getelementptr inbounds %struct.string, %struct.string* %32, i32 0, i32 1
%34 = getelementptr inbounds [1 x i8], [1 x i8]* %33, i64 0, i64 0
%35 = load i8*, i8** %3
%36 = call i8* @strcpy(i8* %34, i8* %35)
%37 = load %struct.string*, %struct.string** @string.list
%38 = load %struct.string*, %struct.string** %4
%39 = getelementptr inbounds %struct.string, %struct.string* %38, i32 0, i32 0
store %struct.string* %37, %struct.string** %39
%40 = load %struct.string*, %struct.string** %4
store %struct.string* %40, %struct.string** @string.list
%41 = load %struct.string*, %struct.string** %4
%42 = getelementptr inbounds %struct.string, %struct.string* %41, i32 0, i32 1
%43 = getelementptr inbounds [1 x i8], [1 x i8]* %42, i64 0, i64 0
store i8* %43, i8** %2
br label %44
44: ; preds = %25, %16
%45 = load i8*, i8** %2
ret i8* %45
}

LLVM IR (-O2):
%struct.string = type { %struct.string*, [1 x i8] }
@string.list = internal unnamed_addr global %struct.string* null
define i8* @string(i8* nocapture %0) {
%2 = load %struct.string*, %struct.string** @string.list
%3 = icmp eq %struct.string* %2, null
br i1 %3, label %13, label %8
4: ; preds = %8
%5 = getelementptr inbounds %struct.string, %struct.string* %9, i64 0, i32 0
%6 = load %struct.string*, %struct.string** %5
%7 = icmp eq %struct.string* %6, null
br i1 %7, label %13, label %8
8: ; preds = %1, %4
%9 = phi %struct.string* [ %6, %4 ], [ %2, %1 ]
%10 = getelementptr inbounds %struct.string, %struct.string* %9, i64 0, i32 1, i64 0
%11 = tail call i32 @strcmp(i8* %10, i8* dereferenceable(1) %0)
%12 = icmp eq i32 %11, 0
br i1 %12, label %23, label %4
13: ; preds = %4, %1
%14 = tail call i64 @strlen(i8* dereferenceable(1) %0)
%15 = add i64 %14, 16
%16 = tail call i32 (i64, ...) bitcast (i32 (...)* @alloc to i32 (i64, ...)*)(i64 %15)
%17 = sext i32 %16 to i64
%18 = inttoptr i64 %17 to %struct.string*
%19 = getelementptr inbounds %struct.string, %struct.string* %18, i64 0, i32 1, i64 0
%20 = tail call i8* @strcpy(i8* %19, i8* dereferenceable(1) %0)
%21 = load %struct.string*, %struct.string** @string.list
%22 = getelementptr inbounds %struct.string, %struct.string* %18, i64 0, i32 0
store %struct.string* %21, %struct.string** %22
store %struct.string* %18, %struct.string** @string.list
br label %23
23: ; preds = %8, %13
%24 = phi i8* [ %19, %13 ], [ %10, %8 ]
ret i8* %24
}