diff -r f25db3e10530 -r 790f4c380343 Array.st --- a/Array.st Fri Jan 17 00:57:14 1997 +0100 +++ b/Array.st Fri Jan 17 15:41:40 1997 +0100 @@ -354,8 +354,7 @@ RETURN ( __InstPtr(slf)->i_instvars[indx] ); } } -%} -. +%}. ^ super basicAt:index ! @@ -388,8 +387,7 @@ RETURN ( anObject ); } } -%} -. +%}. ^ super basicAt:index put:anObject "Modified: 19.4.1996 / 11:14:26 / cg" @@ -501,8 +499,7 @@ RETURN ( nObj ); } } -%} -. +%}. ^ super copyWith:something ! ! @@ -545,64 +542,97 @@ if (nIndex <= actualSize) { if (__isBlockLike(aBlock) - && ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + { + /* + * the most common case: a static compiled block, with home on the stack ... + */ + REGISTER OBJFUNC codeVal; + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { #ifdef NEW_BLOCK_CALL - for (; index < nIndex; index++) { - if (InterruptPending != nil) __interruptL(@line); - - (*codeVal)(aBlock, __InstPtr(self)->i_instvars[index]); - } +# define BLOCK_ARG aBlock #else - home = __BlockInstPtr(aBlock)->b_home; - rHome = home; - if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) { - /* - * home will not move - keep in a fast register - */ -# if defined(UNROLL_LOOPS) - { - int i4; +# define BLOCK_ARG rHome + REGISTER OBJ rHome; + + rHome = __BlockInstPtr(aBlock)->b_home; + if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) +#endif + { + for (; index < nIndex; index++) { + if (InterruptPending != nil) __interruptL(@line); + + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } + RETURN (self); + } + } + } + + /* + * sorry, must check code-pointer in the loop + * it could be recompiled or flushed + */ +# undef BLOCK_ARG +#ifdef NEW_BLOCK_CALL +# define BLOCK_ARG aBlock +# define IBLOCK_ARG nil +#else +# define BLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +# define IBLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +#endif + + for (; index < nIndex; index++) { + REGISTER OBJFUNC codeVal; + + if (InterruptPending != nil) __interruptL(@line); - while ((i4 = index+4) < nIndex) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+1]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+2]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+3]); - index = i4; + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } else { + if (__BlockInstPtr(aBlock)->b_bytecodes != nil) { + /* + * arg is a compiled block with bytecode - + * directly call interpreter without going through Block>>value + */ +#ifdef PASS_ARG_POINTER + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, &(__InstPtr(self)->i_instvars[index])); +#else + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, __InstPtr(self)->i_instvars[index]); +#endif + } else { + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); } - } -# endif - for (; index < nIndex; index++) { - if (InterruptPending != nil) __interruptL(@line); + } + } - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index]); - } - } else { - for (; index < nIndex; index++) { - if (InterruptPending != nil) __interruptL(@line); +# undef BLOCK_ARG +# undef IBLOCK_ARG + + RETURN (self ); + } + + /* + * not a block - send it #value: + */ + for (; index < nIndex; index++) { + if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(home, __InstPtr(self)->i_instvars[index]); - } - } -#endif - } else { - for (; index < nIndex; index++) { - if (InterruptPending != nil) __interruptL(@line); - - (*val.ilc_func)(aBlock, - @symbol(value:), - nil, &val, - __InstPtr(self)->i_instvars[index]); - } + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); } - RETURN (self ); - } + RETURN ( self ); + } } + /* + * I am something, not handle here + */ %}. ^ super do:aBlock ! @@ -626,71 +656,106 @@ slf = self; myClass = __qClass(slf); if (! ((INT)( __ClassInstPtr(myClass)->c_flags) & __MASKSMALLINT(WKPOINTERARRAY)) - && __bothSmallInteger(start, stop)) { - indexLow = __intVal(start); - if (indexLow > 0) { - indexHigh = __intVal(stop); - nIndex = __BYTES2OBJS__(__qSize(slf) - OHDR_SIZE); - if (myClass != @global(Array)) { - nInsts = __intVal(__ClassInstPtr(myClass)->c_ninstvars); - indexLow += nInsts; - indexHigh += nInsts; - } - if (indexHigh <= nIndex) { - indexLow--; - indexHigh--; - if (__isBlockLike(aBlock) - && ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) - && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + && __bothSmallInteger(start, stop) + && ((indexLow = __intVal(start)) > 0)) { + indexHigh = __intVal(stop); + nIndex = __BYTES2OBJS__(__qSize(slf) - OHDR_SIZE); + if (myClass != @global(Array)) { + nInsts = __intVal(__ClassInstPtr(myClass)->c_ninstvars); + indexLow += nInsts; + indexHigh += nInsts; + } + if (indexHigh <= nIndex) { + indexLow--; + indexHigh--; + + if (__isBlockLike(aBlock) + && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + { + /* + * the most common case: a static compiled block, with home on the stack ... + */ + REGISTER OBJFUNC codeVal; + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { #ifdef NEW_BLOCK_CALL - for (index=indexLow; index <= indexHigh; index++) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(aBlock, __InstPtr(self)->i_instvars[index]); - } +# define BLOCK_ARG aBlock #else - home = __BlockInstPtr(aBlock)->b_home; - rHome = home; - if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) { - index = indexLow; -# if defined(UNROLL_LOOPS) - { - int i4; +# define BLOCK_ARG rHome + REGISTER OBJ rHome; + + rHome = __BlockInstPtr(aBlock)->b_home; + if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) +#endif + { + for (; index < nIndex; index++) { + if (InterruptPending != nil) __interruptL(@line); + + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } + RETURN (self); + } + } + } - while ((i4 = index+4) <= indexHigh) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+1]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+2]); - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index+3]); - index = i4; - } - } -# endif - for (; index <= indexHigh; index++) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index]); - } - } else { - for (index=indexLow; index <= indexHigh; index++) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(home, __InstPtr(self)->i_instvars[index]); - } - } + /* + * sorry, must check code-pointer in the loop + * it could be recompiled or flushed + */ +# undef BLOCK_ARG +#ifdef NEW_BLOCK_CALL +# define BLOCK_ARG aBlock +# define IBLOCK_ARG nil +#else +# define BLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +# define IBLOCK_ARG (__BlockInstPtr(aBlock)->b_home) #endif - } else { - for (index=indexLow; index <= indexHigh; index++) { - if (InterruptPending != nil) __interruptL(@line); - (*val.ilc_func) (aBlock, - @symbol(value:), - nil, &val, - __InstPtr(self)->i_instvars[index]); - } - } - } - RETURN ( self ); + + for (; index < nIndex; index++) { + REGISTER OBJFUNC codeVal; + + if (InterruptPending != nil) __interruptL(@line); + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } else { + if (__BlockInstPtr(aBlock)->b_bytecodes != nil) { + /* + * arg is a compiled block with bytecode - + * directly call interpreter without going through Block>>value + */ +#ifdef PASS_ARG_POINTER + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, &(__InstPtr(self)->i_instvars[index])); +#else + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, __InstPtr(self)->i_instvars[index]); +#endif + } else { + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + } + } + +# undef BLOCK_ARG +# undef IBLOCK_ARG + + RETURN (self ); + } + + /* + * not a block - send it #value: + */ + for (; index < nIndex; index++) { + if (InterruptPending != nil) __interruptL(@line); + + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + RETURN ( self ); } } %}. @@ -712,49 +777,102 @@ int indexLow, indexHigh; if (__bothSmallInteger(start, stop) - && (__qClass(self) == @global(Array))) { - indexLow = __intVal(start); - if (indexLow > 0) { - indexHigh = __intVal(stop); - nIndex = __BYTES2OBJS__(__qSize(self) - OHDR_SIZE); - if (indexHigh <= nIndex) { - indexLow--; - indexHigh--; - if (__isBlockLike(aBlock) - && ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) - && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + && (__qClass(self) == @global(Array)) + && ((indexLow = __intVal(start)) > 0)) { + indexHigh = __intVal(stop); + nIndex = __BYTES2OBJS__(__qSize(self) - OHDR_SIZE); + if (indexHigh <= nIndex) { + indexLow--; + indexHigh--; + + if (__isBlockLike(aBlock) + && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + { + /* + * the most common case: a static compiled block, with home on the stack ... + */ + REGISTER OBJFUNC codeVal; + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { #ifdef NEW_BLOCK_CALL - for (index=indexHigh; index >= indexLow; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(aBlock, __InstPtr(self)->i_instvars[index]); - } +# define BLOCK_ARG aBlock +#else +# define BLOCK_ARG rHome + REGISTER OBJ rHome; + + rHome = __BlockInstPtr(aBlock)->b_home; + if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) +#endif + { + for (index=indexHigh; index >= indexLow; index--) { + if (InterruptPending != nil) __interruptL(@line); + + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } + RETURN (self); + } + } + } + + /* + * sorry, must check code-pointer in the loop + * it could be recompiled or flushed + */ +# undef BLOCK_ARG +#ifdef NEW_BLOCK_CALL +# define BLOCK_ARG aBlock +# define IBLOCK_ARG nil #else - home = __BlockInstPtr(aBlock)->b_home; - rHome = home; - if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) { - for (index=indexHigh; index >= indexLow; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(rHome, __InstPtr(self)->i_instvars[index]); - } - } else { - for (index=indexHigh; index >= indexLow; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(home, __InstPtr(self)->i_instvars[index]); - } - } +# define BLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +# define IBLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +#endif + + for (index=indexHigh; index >= indexLow; index--) { + REGISTER OBJFUNC codeVal; + + if (InterruptPending != nil) __interruptL(@line); + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } else { + if (__BlockInstPtr(aBlock)->b_bytecodes != nil) { + /* + * arg is a compiled block with bytecode - + * directly call interpreter without going through Block>>value + */ +#ifdef PASS_ARG_POINTER + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, &(__InstPtr(self)->i_instvars[index])); +#else + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, __InstPtr(self)->i_instvars[index]); #endif - } else { - for (index=indexHigh; index >= indexLow; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*val.ilc_func) (aBlock, - @symbol(value:), - nil, &val, - __InstPtr(self)->i_instvars[index]); - } - } - } - RETURN ( self ); - } + } else { + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + } + } + +# undef BLOCK_ARG +# undef IBLOCK_ARG + + RETURN (self ); + } + + /* + * not a block - send it #value: + */ + for (index=indexHigh; index >= indexLow; index--) { + if (InterruptPending != nil) __interruptL(@line); + + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + RETURN ( self ); + } } %}. ^ super from:start to:stop reverseDo:aBlock @@ -786,52 +904,104 @@ if (nIndex <= actualSize) { if (__isBlockLike(aBlock) - && ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(2))) { + { + /* + * the most common case: a static compiled block, with home on the stack ... + */ + REGISTER OBJFUNC codeVal; + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { #ifdef NEW_BLOCK_CALL - for (; index < nIndex; index++) { - if (InterruptPending != nil) __interruptL(@line); +# define BLOCK_ARG aBlock +#else +# define BLOCK_ARG rHome + REGISTER OBJ rHome; + + rHome = __BlockInstPtr(aBlock)->b_home; + if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) +#endif + { + while (index < nIndex) { + index++; + if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(aBlock, __MKSMALLINT(index+1), - __InstPtr(self)->i_instvars[index]); - } + (*codeVal)(BLOCK_ARG, __MKSMALLINT(index), __InstPtr(self)->i_instvars[index-1]); + } + RETURN (self); + } + } + } + + /* + * sorry, must check code-pointer in the loop + * it could be recompiled or flushed + */ +# undef BLOCK_ARG +#ifdef NEW_BLOCK_CALL +# define BLOCK_ARG aBlock +# define IBLOCK_ARG nil #else - home = __BlockInstPtr(aBlock)->b_home; - rHome = home; - if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) { - /* - * home will not move - keep in a fast register - */ - while (index < nIndex) { - if (InterruptPending != nil) __interruptL(@line); +# define BLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +# define IBLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +#endif + + while (index < nIndex) { + REGISTER OBJFUNC codeVal; + + if (InterruptPending != nil) __interruptL(@line); - index++; - (*codeVal)(rHome, __MKSMALLINT(index), - __InstPtr(self)->i_instvars[index-1]); - } - } else { - while (index < nIndex) { - if (InterruptPending != nil) __interruptL(@line); + index++; + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { + (*codeVal)(BLOCK_ARG, __MKSMALLINT(index), __InstPtr(self)->i_instvars[index-1]); + } else { + if (__BlockInstPtr(aBlock)->b_bytecodes != nil) { + /* + * arg is a compiled block with bytecode - + * directly call interpreter without going through Block>>value + */ +#ifdef PASS_ARG_POINTER + { + OBJ t[2]; - index++; - (*codeVal)(home, __MKSMALLINT(index), - __InstPtr(self)->i_instvars[index-1]); - } - } + t[0] = __MKSMALLINT(index); + t[1] = __InstPtr(self)->i_instvars[index-1]; + + __interpret(aBlock, 2, nil, IBLOCK_ARG, nil, nil, t); + } +#else + __interpret(aBlock, 2, nil, IBLOCK_ARG, nil, nil, __MKSMALLINT(index), __InstPtr(self)->i_instvars[index-1]); #endif - } else { - while (index < nIndex) { - if (InterruptPending != nil) __interruptL(@line); + } else { + (*val2.ilc_func)(aBlock, + @symbol(value:value:), + nil, &val2, + __MKSMALLINT(index), + __InstPtr(self)->i_instvars[index-1]); + } + } + } + +# undef BLOCK_ARG +# undef IBLOCK_ARG - index++; - (*val2.ilc_func)(aBlock, - @symbol(value:value:), - nil, &val2, - __MKSMALLINT(index), - __InstPtr(self)->i_instvars[index-1]); - } + RETURN (self ); } - RETURN (self); + + /* + * not a block - send it #value: + */ + while (index < nIndex) { + if (InterruptPending != nil) __interruptL(@line); + + index++; + (*val2.ilc_func)(aBlock, + @symbol(value:value:), + nil, &val2, + __MKSMALLINT(index), + __InstPtr(self)->i_instvars[index-1]); + } + RETURN ( self ); } } %}. @@ -862,30 +1032,92 @@ if (nIndex <= actualSize) { if (__isBlockLike(aBlock) - && ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) && (__BlockInstPtr(aBlock)->b_nargs == __MKSMALLINT(1))) { + { + /* + * the most common case: a static compiled block, with home on the stack ... + */ + REGISTER OBJFUNC codeVal; + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { #ifdef NEW_BLOCK_CALL - for (index=nIndex-1; index >= endIndex; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(aBlock, __InstPtr(self)->i_instvars[index]); - } +# define BLOCK_ARG aBlock #else - home = __BlockInstPtr(aBlock)->b_home; +# define BLOCK_ARG rHome + REGISTER OBJ rHome; + + rHome = __BlockInstPtr(aBlock)->b_home; + if ((rHome == nil) || (__qSpace(rHome) >= STACKSPACE)) +#endif + { + for (index=nIndex-1; index >= endIndex; index--) { + if (InterruptPending != nil) __interruptL(@line); + + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } + RETURN (self); + } + } + } + + /* + * sorry, must check code-pointer in the loop + * it could be recompiled or flushed + */ +# undef BLOCK_ARG +#ifdef NEW_BLOCK_CALL +# define BLOCK_ARG aBlock +# define IBLOCK_ARG nil +#else +# define BLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +# define IBLOCK_ARG (__BlockInstPtr(aBlock)->b_home) +#endif + for (index=nIndex-1; index >= endIndex; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*codeVal)(home, __InstPtr(self)->i_instvars[index]); - } + REGISTER OBJFUNC codeVal; + + if (InterruptPending != nil) __interruptL(@line); + + if ((codeVal = __BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil) { + (*codeVal)(BLOCK_ARG, __InstPtr(self)->i_instvars[index]); + } else { + if (__BlockInstPtr(aBlock)->b_bytecodes != nil) { + /* + * arg is a compiled block with bytecode - + * directly call interpreter without going through Block>>value + */ +#ifdef PASS_ARG_POINTER + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, &(__InstPtr(self)->i_instvars[index])); +#else + __interpret(aBlock, 1, nil, IBLOCK_ARG, nil, nil, __InstPtr(self)->i_instvars[index]); #endif - } else { - for (index=nIndex-1; index >= endIndex; index--) { - if (InterruptPending != nil) __interruptL(@line); - (*val.ilc_func)(aBlock, - @symbol(value:), - nil, &val, - __InstPtr(self)->i_instvars[index]); - } - } - RETURN (self); + } else { + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + } + } + +# undef BLOCK_ARG +# undef IBLOCK_ARG + + RETURN (self ); + } + + /* + * not a block - send it #value: + */ + for (index=nIndex-1; index >= endIndex; index--) { + if (InterruptPending != nil) __interruptL(@line); + + (*val.ilc_func)(aBlock, + @symbol(value:), + nil, &val, + __InstPtr(self)->i_instvars[index]); + } + RETURN ( self ); } } %}. @@ -943,6 +1175,7 @@ if (index >= 0) { nIndex = __BYTES2OBJS__(__qSize(self) - OHDR_SIZE); endIndex = __intVal(index2) - 1; + if ((endIndex >= index) && (endIndex < nIndex)) { dst = &(__InstPtr(self)->i_instvars[index]); #ifdef memset4 @@ -1339,7 +1572,7 @@ * * therefore, WITH the so-much-blamed goto, we only branch * when found; without the goto, we branch always. - * Pipelined CPUs do usually not like branches. + * Pipelined CPUs do usually not like taken branches. */ #if defined(UNROLL_LOOPS) @@ -1793,5 +2026,5 @@ !Array class methodsFor:'documentation'! version - ^ '$Header: /cvs/stx/stx/libbasic/Array.st,v 1.78 1997-01-16 23:57:14 cg Exp $' + ^ '$Header: /cvs/stx/stx/libbasic/Array.st,v 1.79 1997-01-17 14:41:40 cg Exp $' ! !