#include <string.h>

/* When is strlen() optimized away at compile time?

Test                              -O0  -O1  -O2  -O3  -Os
---------------------------------------------------------
integer_constant                   3    3    3    3    3
direct                             3    3    3    3    3
local_const_array                  3    3    3    3    3
global_const_array                 3    3    3    3    3
static_local_const_array           3    3    3    3    3
static_global_const_array          3    3    3    3    3
local_const_pointer                c    3    3    3    3
local_const_pointer_const          c    3    3    3    3
local_pointer                      c    3    3    3    3
local_pointer_const                c    3    3    3    3
global_pointer_const               c    3    3    3    3
global_const_pointer_const         c    3    3    3    3
static_local_pointer_const         c    3    3    3    3
static_local_const_pointer_const   c    3    3    3    3
static_global_pointer_const        c    3    3    3    3
static_global_const_pointer_const  c    3    3    3    3
local_array                        c    .    !    !    .
static_global_const_pointer        c    .    c    c    .
static_global_pointer              c    .    c    c    .
static_global_array                c    .    c    c    .
static_local_const_pointer         c    .    c    c    .
static_local_pointer               c    .    c    c    .
static_local_array                 c    .    c    c    .
global_const_pointer               c    .    c    c    .
global_pointer                     c    .    c    c    .
global_array                       c    .    c    c    .


Key: There are four ways these can be compiled:

"3" return a constant:
	push   %ebp
	mov    $0x3,%eax
	mov    %esp,%ebp
	pop    %ebp
	ret

"c" Call strlen().  This is a call for an array (calls for pointers
have one extra level of indirection):
	push   %ebp
	mov    %esp,%ebp
	sub    $0x8,%esp
	movl   $0x<foo-thing>,(%esp)
	call   <strlen@plt>
	leave
	ret

"." Inline a small strlen:
	push   %ebp
	mov    %esp,%ebp
	push   %edi
	mov    $0x804a010,%edi
	mov    $0x0,%eax
	mov    $0xffffffff,%ecx
	repnz scas %es:(%edi),%al
	not    %ecx
	lea    -0x1(%ecx),%eax
	pop    %edi
	pop    %ebp

"!" Inline a large strlen:
	push   %ebp
	mov    %esp,%ebp
	push   %ebx
	sub    $0x10,%esp
	mov    0x<foo-thing>,%eax
	lea    -0x8(%ebp),%ebx
	mov    %ebx,%ecx
	mov    %eax,-0x8(%ebp)
label1:	mov    (%ecx),%eax
	add    $0x4,%ecx
	lea    -0x1010101(%eax),%edx
	not    %eax
	and    %eax,%edx
	and    $0x80808080,%edx
	je     label1
	mov    %edx,%eax
	shr    $0x10,%eax
	test   $0x8080,%edx
	cmove  %eax,%edx
	lea    0x2(%ecx),%eax
	cmove  %eax,%ecx
	add    %dl,%dl
	sbb    $0x3,%ecx
	add    $0x10,%esp
	sub    %ebx,%ecx
	mov    %ecx,%eax
	pop    %ebx
	pop    %ebp
	ret


gcc version 4.3.2 on i686-pc-linux-gnu (Gentoo 4.3.2-r3 p1.6, pie-10.1.5)
with GNU libc glibc-2.9_p20081201.

Each block was placed in a separate file for separate compilation.

*/

int integer_constant()
{
	return 3;
}

int direct()
{
	return strlen("foo");
}

int local_const_pointer()
{
	const char *const_foo_pointer = "foo";
	return strlen(const_foo_pointer);
}

int local_const_array()
{
	const char const_foo_array[] = "foo";
	return strlen(const_foo_array);
}

int local_pointer()
{
	char *foo_pointer = "foo";
	return strlen(foo_pointer);
}

int local_array()
{
	char foo_array[] = "foo";
	return strlen(foo_array);
}

const char *global_const_foo_pointer = "foo";
int global_const_pointer()
{
	return strlen(global_const_foo_pointer);
}

const char global_const_foo_array[] = "foo";
int global_const_array()
{
	return strlen(global_const_foo_array);
}

char *global_foo_pointer = "foo";
int global_pointer()
{
	return strlen(global_foo_pointer);
}

char global_foo_array[] = "foo";
int global_array()
{
	return strlen(global_foo_array);
}

int static_local_const_pointer()
{
	static const char *static_const_foo_pointer = "foo";
	return strlen(static_const_foo_pointer);
}

int static_local_const_array()
{
	static const char static_const_foo_array[] = "foo";
	return strlen(static_const_foo_array);
}

int static_local_pointer()
{
	static char *static_foo_pointer = "foo";
	return strlen(static_foo_pointer);
}

int static_local_array()
{
	static char static_foo_array[] = "foo";
	return strlen(static_foo_array);
}

static const char *static_global_const_foo_pointer = "foo";
int static_global_const_pointer()
{
	return strlen(static_global_const_foo_pointer);
}

static const char static_global_const_foo_array[] = "foo";
int static_global_const_array()
{
	return strlen(static_global_const_foo_array);
}

static char *static_global_foo_pointer = "foo";
int static_global_pointer()
{
	return strlen(static_global_foo_pointer);
}

static char static_global_foo_array[] = "foo";
int static_global_array()
{
	return strlen(static_global_foo_array);
}

const char * const global_const_foo_pointer_const = "foo";
int global_const_pointer_const()
{
	return strlen(global_const_foo_pointer_const);
}

char * const global_foo_pointer_const = "foo";
int global_pointer_const()
{
	return strlen(global_foo_pointer_const);
}

int local_const_pointer_const()
{
	const char * const const_foo_pointer_const = "foo";
	return strlen(const_foo_pointer_const);
}

int local_pointer_const()
{
	char * const foo_pointer_const = "foo";
	return strlen(foo_pointer_const);
}

static const char * const static_global_const_foo_pointer_const = "foo";
int static_global_const_pointer_const()
{
	return strlen(static_global_const_foo_pointer_const);
}

static char * const static_global_foo_pointer_const = "foo";
int static_global_pointer_const()
{
	return strlen(static_global_foo_pointer_const);
}

int static_local_const_pointer_const()
{
	static const char * const static_const_foo_pointer_const = "foo";
	return strlen(static_const_foo_pointer_const);
}

int static_local_pointer_const()
{
	static char * const static_foo_pointer_const = "foo";
	return strlen(static_foo_pointer_const);
}

int main()
{
	return
	integer_constant() +
	direct() +
	local_const_pointer() +
	local_const_array() +
	local_pointer() +
	local_array() +
	global_const_pointer() +
	global_const_array() +
	global_pointer() +
	global_array() +
	static_local_const_pointer() +
	static_local_const_array() +
	static_local_pointer() +
	static_local_array() +
	static_global_const_pointer() +
	static_global_const_array() +
	static_global_pointer() +
	static_global_array() +
	global_const_pointer_const() +
	global_pointer_const() +
	local_const_pointer_const() +
	local_pointer_const() +
	static_global_const_pointer_const() +
	static_global_pointer_const() +
	static_local_const_pointer_const() +
	static_local_pointer_const();
}
