#include <exception>
#include <iostream>
#include <stdexcept>

inline void throw_overflow ()
{
	throw std::runtime_error( "Overflow!!!" );
}

#define E void (*err)()

//template <class E>
inline int add( int op1, int op2, E )
{
	asm ( "add %1, %0"
	  	: "=r"(op1) 
		: "r"(op2),"0"(op1) );
	asm goto( "jo %l[throw_exception]"
	  	: : : "memory" 
		: throw_exception );
	return op1;

throw_exception:
	err();
}

//template <class E>
inline long add( long op1, long op2, E)
{
	asm ( "add %1, %0"
	  	: "=r"(op1) 
		: "r"(op2),"0"(op1) );
	asm goto( "jo %l[throw_exception]"
	  	: : : "memory" 
		: throw_exception );
	return op1;

throw_exception:
	err();
}

// Just test a possible implementation for 386
//template <class E>
inline long long add( long long op1, long long op2, E )
{
	unsigned int op1l = (unsigned int)op1;
	unsigned int op1h = (unsigned int)((unsigned long long)op1 >> 32);
	unsigned int op2l = (unsigned int)op2;
	unsigned int op2h = (unsigned int)((unsigned long long)op2 >> 32);
	asm ( "add %2, %0\n"
		  "adc %3, %1"
	  	: "=a"(op1l),"=d"(op1h) 
		: "g"(op2l),"g"(op2h),"0"(op1l),"1"(op1h) );
	asm goto( "jo %l[throw_exception]"
	  	: : : "memory" 
		: throw_exception );
	return (long long)((unsigned long long)op1l | ((unsigned long long)op1h << 32));

throw_exception:
	err();
}

#if 0 
//
//GCC 4.5.2 crashes on this template.
//I filed a bug against GCC. #55528
//
template <class C>
inline C add2( C op1, C op2 )
{
	asm ( "add %1, %0"
	  	: "=r"(op1) 
		: "r"(op2),"0"(op1) );
	asm goto( "jo %l[throw_exception]"
	  	: : : "memory" 
		: throw_exception );
	return op1;

throw_exception:
	return throw_overflow();
}
#endif

template <class C>
inline void test_add( C op1, C op2 )
{try
{
	std::cout << op1 << " + " << op2 << " = ";
	std::cout.flush();
	C r = add( op1, op2, &throw_overflow );
	std::cout << r << std::endl;
}
catch ( const std::exception& e )
{
	std::cout << e.what() << std::endl;
}
catch ( ... )
{
	std::cout << "unknown exception" << std::endl;
}
}

int main( void )
{
	test_add( 10, 20);
	test_add( 100, 200);
	test_add( 1000, 2000);
	test_add(  1000000000,  2000000000);
	test_add(  1000000000, -2000000000);
	test_add( -1000000000,  2000000000);
	test_add( -1000000000, -2000000000);
	test_add(  1000000000L, 2000000000L);
	test_add(  1000000000L,-2000000000L);
	test_add( -1000000000L, 2000000000L);
	test_add( -1000000000L,-2000000000L);
	test_add(  7000000000000000000L,  8000000000000000000L);
	test_add(  7000000000000000000L, -8000000000000000000L);
	test_add( -7000000000000000000L,  8000000000000000000L);
	test_add( -7000000000000000000L, -8000000000000000000L);
	test_add(  (long long)7000000000000000000L,  (long long)8000000000000000000L);
	test_add(  (long long)7000000000000000000L, (long long)-8000000000000000000L);
	test_add( (long long)-7000000000000000000L,  (long long)8000000000000000000L);
	test_add( (long long)-7000000000000000000L, (long long)-8000000000000000000L);
	return 0;
}

