user31105: In the (official) file wiring_shift.c I found the following code for shiftOut:
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(dataPin, !!(val & (1 << i)));
        else    
            digitalWrite(dataPin, !!(val & (1 << (7 - i))));

        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);        
    }
} 

However, the last two digitalWrite commands occur directly after each other. I wouldn't hope that digitalWrite depends on some time or delay, so how can it be assumed that the slave device sees the clockPin is HIGH if the Arduino immediately sets the clockPin to LOW again?
(My plan is to convert the shiftIn/shiftOut code to STM32 using HAL/CubeIDE).

user313: Some time ago, I timed digitalWrite() and direct port write commands by looping over 10,000 executions of each and timing them with millis(). I made both tests on a 16MHz Atmega 328p at 16MHz.
For digitalWrite(13, LOW), I got 2.7usec / call.
For PORTB &= 0x20;, I got 0.15usec / statement. (The direct port access was executed 10 times within the loop to further pad it out for the millis() result to have any significance.)
[In my notes I wrote that I timed PORTB &= 0x20;, but the correct equivalent to the digitalWrite() version is PORTB &= ~0x20;. That is probably what I actually did, but note that the bit-mask would be generated at compile time, so either way should have produced the same result.]

how can it be assumed that the slave device sees the clockPin is HIGH

It would depend on the specifics of the slave, but it is quite likely triggered by a pulse, rather than polling its input until it finds a high-level.
Update:
To confirm my timing data, I redid my 2.5-years-ago experiment. I put 10 statements in each of 2 loops of 10,000 iterations, either:
digitalWrite(13, LOW);

or
PORTB &= ~0x20;

And got these total times in msec for the 100,000 executions:
digitalWrite(): 305     // digitalWrite(13, LOW);
PORTB (correct): 15     // PORTB &= ~0x20;
These translate to single execution times in 10s of nanoseconds:
3050 nsec == 3.05 usec for the function call,
150 nsec for direct port I/O, making port I/O about 20x faster.

31105: <p>In the (official) file <code>wiring_shift.c</code> I found the following code for shiftOut:</p>

<pre><code>void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i &lt; 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(dataPin, !!(val &amp; (1 &lt;&lt; i)));
        else    
            digitalWrite(dataPin, !!(val &amp; (1 &lt;&lt; (7 - i))));

        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);        
    }
} 
</code></pre>

<p>However, the last two <code>digitalWrite</code> commands occur directly after each other. I wouldn't hope that <code>digitalWrite</code> depends on some time or delay, so how can it be assumed that the slave device sees the clockPin is HIGH if the Arduino immediately sets the clockPin to LOW again?</p>

<p>(My plan is to convert the shiftIn/shiftOut code to STM32 using HAL/CubeIDE).</p>
 313: <p>Some time ago, I timed digitalWrite() and direct port write commands by looping over 10,000 executions of each and timing them with millis(). I made both tests on a 16MHz Atmega 328p at 16MHz.</p>

<p>For <code>digitalWrite(13, LOW)</code>, I got 2.7usec / call.<br>
For <code>PORTB &amp;= 0x20;</code>, I got 0.15usec / statement. (The direct port access was executed 10 times within the loop to further pad it out for the <code>millis()</code> result to have any significance.)</p>

<p>[In my notes I wrote that I timed <code>PORTB &amp;= 0x20;</code>, but the correct equivalent to the <code>digitalWrite()</code> version is <code>PORTB &amp;= ~0x20;</code>. That is probably what I actually did, but note that the bit-mask would be generated at compile time, so either way should have produced the same result.]</p>

<blockquote>
  <p>how can it be assumed that the slave device sees the clockPin is HIGH</p>
</blockquote>

<p>It would depend on the specifics of the slave, but it is quite likely triggered by a pulse, rather than polling its input until it finds a high-level.</p>

<p>Update:
To confirm my timing data, I redid my 2.5-years-ago experiment. I put 10 statements in each of 2 loops of 10,000 iterations, either:</p>

<pre><code>digitalWrite(13, LOW);
</code></pre>

<p>or</p>

<pre><code>PORTB &amp;= ~0x20;
</code></pre>

<p>And got these total times in msec for the 100,000 executions:</p>

<p>digitalWrite(): 305     // digitalWrite(13, LOW);
PORTB (correct): 15     // PORTB &amp;= ~0x20;</p>

<p>These translate to single execution times in 10s of nanoseconds:<br>
3050 nsec == 3.05 usec for the function call,<br>
150 nsec for direct port I/O, making port I/O about 20x faster.</p>
