This is more an opinion/comment than an answer.
You don't want and shouldn't be programming in C. C++, when used in the right way, is far superior. (OK, I have to admit, when used in the wrong way it is far worse than C.) That limits you to chips that have a (modern) C++ compiler, which is roughly evertything that is supported by GCC, including AVR (with some limitations, filo mentions the problems of a non-uniform address space), but excluding nearly all PICs (PIC32 could be supported, but I haven't seen any decent port yet).
When you are programming algorithms in C/C++ the difference between the choices you mention is small (except that an 8 or 16 bit chip will be at a severe disadvantage when you do a lot of 16, 32 or higher bit arithmetic). When you need the last ounce of performance, you will probably need to use assembler (either your own or code provided by the vendor or a third party). In that case you might want to re-consider the chip you selected.
When you are coding to the hardware you can either use some abstraction layer (often provided by the manufacturer) or write your own (based on the datasheet and/or example code). IME existing C abstractions (mbed, cmsis, ...) are often functionaly (almost) correct, but fail horribly in performance (check oldfarts rant about 6 layers of indirection for a pin set operation), usability and portability. They want to expose all functionality of the particular chip to you, which in nearly all cases you won't need and rather not care about, and it locks your code to that particular vendor (and probably that particular chip).
This is were C++ can do much better: when done properly, a pin set can go through 6 or more abstraction layers (because that makes a better (portable!) interface and shorter code possible), yet provide an interface that is target-independent for the simple cases, and still result in the same machine code as you would write in assembler.
A snippet of the coding style I use, which can either make you enthousiastic or turn away in horror:
// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };
template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {
static void direction_set_direct( pin_direction d ){
( ( d == pin_direction::input )
? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER ) = ( 0x1U << pin );
}
static void set_direct( bool v ){
( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR ) = ( 0x1U << pin );
}
};
// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;
// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led = invert< pin_out< _led > >;
In reality there are some more layers of abstraction. Yet the final use of the led, let's say to turn it on, doesn't show the complexity or the details of the target (for an arduin uno or an ST32 blue pill the code would be identical).
target::led::init();
target::led::set( 1 );
The compiler isn't intimidated by all those layers, and because there are no virtual functions involved the optimizer sees through everything (some details, omitted, like enabling the peripheral clock):
mov.w r2, #134217728 ; 0x8000000
ldr r3, [pc, #24]
str r2, [r3, #16]
str r2, [r3, #48]
Which is how I would have written it in assembler - IF I had realized that the PIO registers can be used with offsets from a common base. In this case I probably would, but the compiler is far better at optimizing such things than I am.
So as far as I have an answer, it is: write an abstraction layer for your hardware, but do it in modern C++ (concepts, templates) so it doesn't harm your performance. With that in place, you can switch easily to another chip. You can even start developing on some random chip you have laying around, are familiair with, have good debugging tools for, etc. and postpone the final choice untill later (when you have more info about the required memory, CPU speed etc.).
IMO one of the falacies of embbeded development is choosing the chip first (it is a question often asked on this forum: which chip should I choose for .... The best answer is generally: it doesn't matter.)
(edit - response to "So performance wise, C or C++ would be at same level?")
For the same constructs, C and C++ are the same. C++ has much more constructs for abstraction (just a few: classes, templates, constexpr) which can, like any tool, be used for the good or for the bad. To make the discussions more interesting: not everyone agrees what is good or bad...