
#undef DEBUG_PROFILER

#define ALLOCATION_PROFILE
#define MODULE_NAMES

#define d0 %eax
#define d1 %ebx
#define a0 %ecx
#define a1 %edx
#define a2 %ebp
#define a3 %esi
#define a4 %edi
#define sp %esp

#if defined(_WINDOWS_)
# define	align(n) .align (1<<n)
#else
# define	align(n) .align n
#endif

	.global	init_profiler
	.global	profile_r
	.global profile_l
	.global profile_l2
	.global profile_n
	.global profile_n2
	.global profile_s
	.global profile_s2
	.global profile_t
	.global	write_profile_information
	.global	write_profile_stack

	.global	@allocate_memory
	.global	__STRING__
	.global	openF
	.global	closeF
	.global	writeFC
	.global	writeFI
	.global	print_error
	.global	@ab_stack_size
	.global	@ew_print_string
	.global	@ew_print_char
	.global	@create_profile_file_name

#define	next		0
#define	time_hi		4
#define	time_lo		8
#define	n_profiler_calls	12
#define	n_strict_calls	16
#define	n_lazy_calls	20
#define	n_curried_calls	24
#define	n_bytes_allocated	28
#define	name		32
#define	FunctionProfile	36
	
	.text
profile_t:
	push	d0
	push	a1
	rdtsc

	push	a0
	mov	global_stack_pointer,a0

	push	d1	
	mov	-4(a0),d1
	
	sub	$4,a0
	mov	d1,global_last_tail_call

	mov	a0,global_stack_pointer

	sub	global_time_lo,d0	
	sbb	global_time_hi,a1

	add	d0,time_lo(d1)	
	adc	a1,time_hi(d1)
	
	incl	n_profiler_calls(d1)

#ifdef ALLOCATION_PROFILE
 	mov	end_heap,a0
	sub	a4,a0
	mov	global_n_bytes_free,d0
	add	$32,a0	
	sub	a0,d0	
	mov	a0,global_n_bytes_free
	add	d0,n_bytes_allocated(d1)
#endif

	pop	d1
	pop	a0

	rdtsc
	mov	a1,global_time_hi
	pop	a1
	mov	d0,global_time_lo
	pop	d0
	ret

profile_r:
	push	d0
	push	a1
	rdtsc

	push	a0
	mov	global_stack_pointer,a0

	push	d1	
	mov	-4(a0),d1

	sub	$4,a0
	movl	$0,global_last_tail_call

	mov	a0,global_stack_pointer

	sub	global_time_lo,d0
	sbb	global_time_hi,a1

	add	d0,time_lo(d1)
	adc	a1,time_hi(d1)

	incl	n_profiler_calls(d1)

#ifdef ALLOCATION_PROFILE
 	mov	end_heap,a0
	sub	a4,a0
	mov	global_n_bytes_free,d0
	add	$32,a0	
	sub	a0,d0	
	mov	a0,global_n_bytes_free
	add	d0,n_bytes_allocated(d1)
#endif

	pop	d1
	pop	a0

	rdtsc
	mov	a1,global_time_hi
	pop	a1
	mov	d0,global_time_lo
	pop	d0
	ret

profile_l:
	push	d0
	push	a1
	rdtsc

	push	d1
	mov	(a2),d1

	test	d1,d1
	je	allocate_function_profile_record_l
allocate_function_profile_record_lr:
	push	a0
	
	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0

	test	a2,a2
	jne	use_tail_calling_function_l

	mov	-4(a0),a2
use_tail_calling_function_lr:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	mov	d1,(a0)
	add	$4,a0
	
	incl	n_curried_calls(d1)
	jmp	profile_n_

allocate_function_profile_record_l:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_lr

use_tail_calling_function_l:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_lr

profile_l2:
	push	d0
	push	a1
	rdtsc

	push	d1
	mov	(a2),d1

	test	d1,d1
	je	allocate_function_profile_record_l2
allocate_function_profile_record_l2r:
	push	a0
	
	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0
	
	test	a2,a2
	jne	use_tail_calling_function_l2

	mov	-4(a0),a2
use_tail_calling_function_l2r:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	mov	d1,(a0)
	mov	d1,4(a0)
	add	$8,a0

	incl	n_curried_calls(d1)
	jmp	profile_n_

allocate_function_profile_record_l2:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_l2r

use_tail_calling_function_l2:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_l2r

profile_n:
	push	d0
	push	a1
	rdtsc
		
	push	d1
	mov	(a2),d1
	
	test	d1,d1
	je	allocate_function_profile_record_n
allocate_function_profile_record_nr:
	push	a0
	
	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0

	test	a2,a2
	jne	use_tail_calling_function_n

	mov	-4(a0),a2
use_tail_calling_function_nr:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	mov	d1,(a0)
	add	$4,a0

	incl	n_lazy_calls(d1)
	jmp	profile_n_

allocate_function_profile_record_n:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_nr

use_tail_calling_function_n:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_nr

profile_n2:
	push	d0
	push	a1
	rdtsc

	push	d1
	mov	(a2),d1

	test	d1,d1
	je	allocate_function_profile_record_n2
allocate_function_profile_record_n2r:
	push	a0
	
	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0

	test	a2,a2
	jne	use_tail_calling_function_n2

	mov	-4(a0),a2
use_tail_calling_function_n2r:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	mov	d1,(a0)
	mov	d1,4(a0)
	add	$8,a0

	incl	n_lazy_calls(d1)
	jmp	profile_n_

allocate_function_profile_record_n2:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_n2r

use_tail_calling_function_n2:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_n2r

profile_s2:
	push	d0
	push	a1
	rdtsc
		
	push	d1
	mov	(a2),d1
	
	test	d1,d1
	je	allocate_function_profile_record_s2
allocate_function_profile_record_s2r:
	push	a0

	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0
	
	test	a2,a2
	jne	use_tail_calling_function_s2

	mov	-4(a0),a2
use_tail_calling_function_s2r:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	movl	d1,(a0)
	movl	d1,4(a0)
	add	$8,a0
	jmp	profile_s_

allocate_function_profile_record_s2:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_s2r

use_tail_calling_function_s2:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_s2r

profile_s:
	push	d0
	push	a1
	rdtsc
	
	push	d1
	movl	(a2),d1
	
	test	d1,d1
	je	allocate_function_profile_record_s
allocate_function_profile_record_sr:
	push	a0

	mov	global_last_tail_call,a2
	mov	global_stack_pointer,a0
	
	test	a2,a2
	jne	use_tail_calling_function_s

	mov	-4(a0),a2
use_tail_calling_function_sr:

#ifdef DEBUG_PROFILER
	testl	(d1),d1
#endif
	movl	d1,(a0)
	add	$4,a0

profile_s_:
	incl	n_strict_calls(d1)

profile_n_:
	mov	a0,global_stack_pointer

	sub	global_time_lo,d0
	sbb	global_time_hi,a1

	add	d0,time_lo(a2)
	adc	a1,time_hi(a2)

	incl	n_profiler_calls(d1)

#ifdef ALLOCATION_PROFILE
 	mov	end_heap,a0
	sub	a4,a0
	mov	global_n_bytes_free,d0
	add	$32,a0	
	sub	a0,d0	
	mov	a0,global_n_bytes_free
	add	d0,n_bytes_allocated(a2)
#endif

	pop	a0
	pop	d1

	rdtsc
	mov	a1,global_time_hi
	pop	a1
	mov	d0,global_time_lo
	pop	d0
	ret

allocate_function_profile_record_s:
	call	allocate_function_profile_record
	jmp	allocate_function_profile_record_sr

use_tail_calling_function_s:
	movl	$0,global_last_tail_call
	jmp	use_tail_calling_function_sr


/ argument: a2: function name adress-4
/ result:   d1: function profile record adress

allocate_function_profile_record:
	push	d0
	mov	global_n_free_records_in_block,d0
	mov	global_last_allocated_block,d1

	test	d0,d0
	jne	no_alloc

	push	d1
	push	a0
	push	a1

	pushl	$128*FunctionProfile
	call	@allocate_memory
	add	$4,sp

	test	d0,d0

	pop	a1
	pop	a0
	pop	d1

	je	no_memory

	mov	d0,d1
	mov	$128,d0
	mov	d1,global_last_allocated_block

no_alloc:	
	dec	d0
	mov	d0,global_n_free_records_in_block
	lea	FunctionProfile(d1),d0
	mov	d0,global_last_allocated_block

	xor	d0,d0
	mov	d0,time_hi(d1)
	mov	d0,time_lo(d1)
	mov	d0,n_profiler_calls(d1)
	mov	d0,n_strict_calls(d1)
	mov	d0,n_lazy_calls(d1)
	mov	d0,n_curried_calls(d1)
	mov	d0,n_bytes_allocated(d1)

	mov	global_profile_records,d0
	mov	a2,name(d1)

	mov	d0,next(d1)
	mov	d1,global_profile_records
	
	mov	d1,(a2)
	pop	d0
	ret

no_memory:
	movl	$not_enough_memory_for_profiler,a2
	pop	d0
	jmp	print_error

write_profile_information:
	pushl	$profile_file_name
	call	@create_profile_file_name
	addl	$4,sp

	mov	$1,d0
	mov	$profile_file_name,a0
	call	openF

	pop	a0
	test	d1,d1
	je	cannot_open
	
	mov	global_profile_records,a2

write_profile_lp:	
	test	a2,a2
	je	end_list

	mov	name(a2),a1
	push	a2

#ifdef MODULE_NAMES
	push	a1
	
	movl	-4(a1),a1
	movl	(a1),d1
	addl	$4,a1

write_module_name_lp:
	subl	$1,d1
	jc	end_module_name

	pushl	d1
	movzbl	(a1),d1
	pushl	a1

	pushl	$l0
	pushl	a0
	jmp	writeFC
l0:
	popl	a1
	movl	d0,a0
	movl	d1,d0
	popl	d1
	addl	$1,a1
	jmp	write_module_name_lp
	
end_module_name:
	mov	$' ',d1
	push	$l00
	push	a0
	jmp	writeFC
l00:	mov	d0,a0
	mov	d1,d0

	pop	a1
#endif

	add	$3,a1
	
write_function_name_lp:
	movzbl	1(a1),d1
	add	$1,a1

	test	d1,d1
	je	end_function_name

	push	a1

	push	$l1
	push	a0
	jmp	writeFC
l1:	mov	d0,a0
	mov	d1,d0

	pop	a1
	jmp	write_function_name_lp

end_function_name:
	mov	$' ',d1
	push	$l2
	push	a0
	jmp	writeFC
l2:	mov	d0,a0
	mov	d1,d0

	mov	(sp),d1
	mov	n_strict_calls(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	n_lazy_calls(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	n_curried_calls(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	n_profiler_calls(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	n_bytes_allocated(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	time_hi(d1),d1
	call	writeFI_space

	mov	(sp),d1
	mov	time_lo(d1),d1

	pushl	$l3
	push	a0
	jmp	writeFI
l3:	mov	d0,a0
	mov	d1,d0

	mov	$10,d1
	pushl	$l4
	push	a0
	jmp	writeFC
l4:	mov	d0,a0
	mov	d1,d0

	pop	a2
	mov	next(a2),a2
	jmp	write_profile_lp

writeFI_space:
	pushl	$l5
	push	a0
	jmp	writeFI
l5:	mov	d0,a0
	mov	d1,d0

	push	$l6
	push	a0
	mov	$' ',d1
	jmp	writeFC
l6:	mov	d0,a0
	mov	d1,d0
	ret

end_list:
	mov	d0,d1
	call	closeF

cannot_open:
	ret
	
write_profile_stack:
	mov	global_stack_pointer,d0

	test	d0,d0
	je	stack_not_initialised

	push	d0
	
	push	$stack_trace_string
	call	@ew_print_string
	add	$4,sp
	
	pop	d0
	
/	mov	$12,a2
	movl	@stack_trace_depth,a2
write_functions_on_stack:
	mov	-4(d0),d1
	sub	$4,d0

	test	d1,d1
	je	end_profile_stack

	push	d0
	mov	name(d1),a0

	push	a2
	add	$4,a0

	push	a0
	call	@ew_print_string
	add	$4,sp

	pushl	$10
	call	@ew_print_char
	add	$4,sp

	pop	a2
	pop	d0

	sub	$1,a2
	jne	write_functions_on_stack
	
end_profile_stack:
stack_not_initialised:
	ret

init_profiler:
	pushfl
	movl	$0x200000,%eax
	pop	%ebx
	xor	%ebx,%eax
	push	%eax
	popfl
	pushfl
	pop	%eax
	xor	%ebx,%eax
	jz	no_tsc_error
	
	movl	$1,%eax
	cpuid
	andl	$16,%edx
	jz	no_tsc_error

	pushl	@ab_stack_size
	call	@allocate_memory
	add	$4,sp
	
	test	d0,d0
	je	init_profiler_error

	push	d0
	
	mov	$start_string,a2
	call	allocate_function_profile_record

	pop	a1

	mov	d1,4(a1)
	movl	$0,(a1)
	add	$8,a1
	mov	a1,global_stack_pointer
	movl	$0,global_last_tail_call

 	mov	end_heap,a1
	sub	a4,a1
	add	$32,a1	
	mov	a1,global_n_bytes_free

	rdtsc
	mov	a1,global_time_hi
	mov	d0,global_time_lo
	ret

no_tsc_error:
	movl	$0,global_stack_pointer
	movl	$no_tsc_error_string,a2
	jmp	print_error

init_profiler_error:
	movl	$0,global_stack_pointer
	movl	$not_enough_memory_for_profile_stack,a2
	jmp	print_error

	.data
	align (2)

global_n_free_records_in_block:	.long 0
/ 0 n free records in block
global_last_allocated_block:	.long 0
/ 4 latest allocated block
global_profile_records:		.long 0
/ 8 profile record list
global_time_hi:			.long 0
/ 12 clock
global_time_lo:			.long 0	
global_stack_pointer:		.long 0
/ 20 stack pointer
global_last_tail_call:		.long 0
/ 24 last tail calling function
global_n_bytes_free:		.long 0	

profile_file_name:
	.long	__STRING__+2
	.long	0
	.long	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.long	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.long	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.long	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.long	0
	align	(2)
#ifdef MODULE_NAMES
# if 0
/ m_system also defined in cgistartup.s
m_system:
	.long	6
	.ascii	"System"
	.byte	0
	.byte	0
# endif
	.long	m_system
#endif
@stack_trace_depth:
	.long	12
start_string:
	.long	0
	.asciz	"start"
	align	(2)
no_tsc_error_string:
	.ascii	"cannot profile because this processor does not have a time stamp counter"
	.byte	10
	.byte	0
not_enough_memory_for_profile_stack:
	.ascii	"not enough memory for profile stack"
	.byte	10
	.byte	0
not_enough_memory_for_profiler:
	.ascii	"not enough memory for profiler"
	.byte	10
	.byte	0
stack_trace_string:
	.ascii	"Stack trace:"
	.byte	10
	.byte	0

	align	(2)
