#include "testcode.h"

   /* Test a simple loop of 1 million instructions             */
   /* Most implementations should count be correct within 1%   */
   /* This loop in in assembly language, as compiler generated */
   /* code varies too much.                                    */

int instructions_million(void) {

#if defined(__i386__) || (defined __x86_64__)
	asm(	"	xor	%%ecx,%%ecx\n"
		"	mov	$499999,%%ecx\n"
		"test_loop:\n"
		"	dec	%%ecx\n"
		"	jnz	test_loop\n"
		: /* no output registers */
		: /* no inputs */
		: "cc", "%ecx" /* clobbered */
	);
	return 0;
#elif defined(__PPC__)
	asm(	"	nop			# to give us an even million\n"
		"	lis	15,499997@ha	# load high 16-bits of counter\n"
		"	addi	15,15,499997@l	# load low 16-bits of counter\n"
		"55:\n"
		"	addic.  15,15,-1              # decrement counter\n"
		"	bne     0,55b                  # loop until zero\n"
		: /* no output registers */
		: /* no inputs */
		: "cc", "15" /* clobbered */
	);
	return 0;
#elif defined(__ia64__)

	asm(	"	mov	loc6=166666	// below is 6 instr.\n"
		"	;;			// because of that we count 4 too few\n"
		"55:\n"
		"	add	loc6=-1,loc6	// decrement count\n"
		"	;;\n"
		"	cmp.ne	p2,p3=0,loc6\n"
		"(p2)	br.cond.dptk	55b	// if not zero, loop\n"
		: /* no output registers */
		: /* no inputs */
		: "p2", "loc6" /* clobbered */
	);
	return 0;
#elif defined(__sparc__)
	asm(	"	sethi	%%hi(333333), %%l0\n"
		"	or	%%l0,%%lo(333333),%%l0\n"
		"test_loop:\n"
		"	deccc	%%l0		! decrement count\n"
		"	bnz	test_loop	! repeat until zero\n"
		"	nop			! branch delay slot\n"
		: /* no output registers */
		: /* no inputs */
		: "cc", "l0" /* clobbered */
	);
	return 0;
#elif defined(__arm__)
	asm(	"	ldr	r2,count	@ set count\n"
		"	b       test_loop\n"
		"count:	.word 333332\n"
		"test_loop:\n"
		"	add	r2,r2,#-1\n"
		"	cmp	r2,#0\n"
		"	bne	test_loop	@ repeat till zero\n"
		: /* no output registers */
		: /* no inputs */
		: "cc", "r2" /* clobbered */
	);
	return 0;
#elif defined(__aarch64__)
	asm(	"	ldr	x2,=333332	// set count\n"
		"test_loop:\n"
		"	add	x2,x2,#-1\n"
		"	cmp	x2,#0\n"
		"	bne	test_loop	// repeat till zero\n"
		: /* no output registers */
		: /* no inputs */
		: "cc", "r2" /* clobbered */
	);
	return 0;
#endif

	return CODE_UNIMPLEMENTED;

}


/* fldcw instructions are counted oddly on Pentium 4 machines */

int instructions_fldcw(void) {

#if defined(__i386__) || (defined __x86_64__)

	int saved_cw,result,cw;
	double three=3.0;

	asm(	"	mov	$100000,%%ecx\n"
		"big_loop:\n"
		"	fldl	%1		# load value onto fp stack\n"
		"	fnstcw	%0		# store control word to mem\n"
		"	movzwl	%0, %%eax	# load cw from mem, zero extending\n"
		"	movb	$12, %%ah	# set cw for \"round to zero\"\n"
		"	movw	%%ax, %3	# store back to memory\n"
		"	fldcw	%3		# save new rounding mode\n"
		"	fistpl	%2		# save stack value as integer to mem\n"
		"	fldcw	%0		# restore old cw\n"
		"	loop	big_loop	# loop to make the count more obvious\n"
		: /* no output registers */
		: "m"(saved_cw), "m"(three), "m"(result), "m"(cw) /* inputs */
		: "cc", "%ecx","%eax" /* clobbered */
	);
    return 0;
#endif

	return CODE_UNIMPLEMENTED;
}


/* rep instructions are counted a bit non-intuitively */
/* some tools like Valgrind and Pin may count differently than real hardware */

int instructions_rep(void) {

#if defined(__i386__) || defined(__ILP32__)

	char buffer_out[16384];

	asm(	"	mov	$1000,%%edx\n"
		"	cld\n"
		"loadstore:			# test 8-bit store\n"
		"	mov	$0xd, %%al	# set eax to d\n"
		"	mov	$16384, %%ecx\n"
		"	mov	%0, %%edi	# set destination\n"
		"	rep	stosb		# store d 16384 times, auto-increment\n"
		"	dec	%%edx\n"
		"	jnz	loadstore\n"
		: /* outputs */
		: "rm" (buffer_out) /* inputs */
		: "cc", "%esi","%edi","%edx","%ecx","%eax","memory" /* clobbered */
	);
	return 0;
#elif defined (__x86_64__)

	char buffer_out[16384];

	asm(	"	mov	$1000,%%edx\n"
		"	cld\n"
		"loadstore:			# test 8-bit store\n"
		"	mov	$0xd, %%al	# set eax to d\n"
		"	mov	$16384, %%ecx\n"
		"	mov	%0, %%rdi	# set destination\n"
		"	rep	stosb		# store d 16384 times, auto-increment\n"
		"	dec	%%edx\n"
		"	jnz	loadstore\n"
		: /* outputs */
		: "rm" (buffer_out) /* inputs */
		: "cc", "%esi","%edi","%edx","%ecx","%eax","memory" /* clobbered */
	);
	return 0;
#endif

	return CODE_UNIMPLEMENTED;

}
