Si eso esta claro, si quitas el delay_ms(4000) desaparece el warning al desaparecer la recursividad (llamar a la funcion delay_ms desde dentro de otra delay_ms).
La duda es si mantenemos los dos delay_ms porque se ejecuta el codigo de la interrupcion si se supone que el compilador ha deshabilitado las interrupciones.
O las deshabilita cuando le da la gana? 
Yo creería que las deshabilita al momento de llamar a la función (justo antes o después de poner los parámetros de la función en la pila) y las vuelve a habilitar luego de retornar de la función.
Es decir: las interrupciones están deshabilitadas solo cuando se ejecuta la función delay().
¿Pero antes mencionabas que la interrupción salta mientras se está ejecutando el delay o entendí mal?.
¿Como verificaste eso? (breakpoint dentro de rutina de interrupción?).
De todas formas no es aconsejable usar un retardo de 4 segundos dentro de una interrupción porque el programa se va a quedar "clavado" ahí y no va a tener respuesta a cualquier estímulo externo en ese estado, pero siendo un programa de pruebas.
Para aclarar términos: reentrada no se refiere a lo mismo que recursividad. En C para micros la recursividad no está en general soportada porque la pila se llenaría al hacer un número indeterminado de llamadas a función anidadas. Hay excepciones (llamada a función recursiva como última instrucción de la función recursiva para reutilizar la pila) pero no es nada frecuente verlo.
Reentrada se refiere a qué pasa cuando una función se llama desde 2 o más hilos/tareas/threads a la misma vez. Llevado al caso de los micros sin usar RTOS ni ninguna de esas yerbas, solo hay 2 hilos posibles: el código normalito del main, y el código de interrupción.
Bueno... creo que algunos pics tienen interrupción de alta prioridad y baja prioridad y que la de alta puede interrumpir a la de baja... ahí sería 3 hilos.
Entonces, si una función escribe variables globales (variables del programa, puertos, periféricos) muy probablemente no sea re-entrante, y por eso el compilador - dado que el posible conflicto solo es entre código de interrupción y código normal - introduce la deshabilitación de interrupción antes de llamarla y habilitación luego de retornar como forma básica de evitar problemas de reentrada para este caso particular de microcontrolador sin ningún sistema operativo (la función siempre se ejecuta de un tirón sin ser interrumpida por nadie).
Conclusión: creo que el compilador deshabilita interrupciones antes de llamar a delay() y las vuelve a habilitar luego del retorno de delay(). Introduce ese código en el prólogo (deshabilitar) y epílogo (habilitar) de la función.
Como probar si la rutina delay está siendo interrumpida?, con alguna trampa de prueba como:
volatile unsigned int delayActivo = 0;
void MiDelay(unsigned long cuentas) {
if(delayActivo) {
//poner un punto de interrupción o while(1) para colgar a propósito el programa
//esto significaría que se llamó a MiDelay() desde la interrupción cuando
//se estaba ejecutando el MiDelay del while(1)
//Si el compilador hizo bien su trabajo este punto nunca debería alcanzarse.
} else {
delayActivo = 1;
delay(cuentas);
}
}
void main(void) {
//...
while(1) {
//...
MiDelay(blabla);
}
}
void Interrupcion(void){
//...
MiDelay(blabla2);
}