20221222

RISC-V pair programming with ChatGPT

In spite of having full confidence of being able to spend some good memorable times with family and friends during this Christmas, the month of December 2022 will also be remembered by me as the time when:

  • Fusion energy yielded net positive results.

  • ChatGPT was made available for the masses to evaluate.


In this session I ask ChatGPT to write some RISC-V assembly. One needs to be very explicit to avoid some common mistakes ChatGPT makes on the task, like not knowing how to handle floating point operands and treating them as integers instead. This tendency reflects the likely fact that most examples it has seen did only use integer arithmetic. However, ChatGPT manages to generate valid code when hinted appropriately.


Once it has been able to generate valid code I make suggestions on how to optimise the code. For my amusement, ChatGPT understands my instructions and does implement them, leading to a faster code.



Abel:


Can you translate the following function into RISC-V assembly?


void madd(size_t size, float* dst, float* a, float* b, float* b) {

  for (size_t i = 0; i < size; ++i) {

    dst[i] = a[i] + b[i]*c[i];

  }

}


Please note that the arithmetic operations inside of the loop should be done in floating point. For example, you should use flw.s rather than lw for loading operands, when you feel that is better fitting.


Also, please exploit the fmadd.s instruction that perfectly fits in this use case.


ChatGPT:


Here is the RISC-V assembly code for the madd function:

# RISC-V assembly for the madd function


# Load the arguments

la a0, a           # load the address of a into a0

la a1, b           # load the address of b into a1

la a2, c           # load the address of c into a2

la a3, dst         # load the address of dst into a3

li a4, size        # load the value of size into a4


# Set up the loop

addi a5, zero, 0   # initialise the loop counter to 0


loop:

  # Load the operands

  flw.s f0, 0(a0)   # load a[i] into f0

  flw.s f1, 0(a1)   # load b[i] into f1

  flw.s f2, 0(a2)   # load c[i] into f2


  # Perform the floating point multiplication

  fmul.s f3, f1, f2 # compute b[i]*c[i] and store the result in f3


  # Perform the floating point addition

  fadd.s f4, f0, f3 # compute a[i] + b[i]*c[i] and store the result in f4


  # Store the result

  fsw.s f4, 0(a3)   # store the result in dst[i]


  # Increment the loop counter and the pointers

  addi a5, a5, 1    # increment the loop counter

  addi a0, a0, 4    # increment the pointer to a

  addi a1, a1, 4    # increment the pointer to b

  addi a2, a2, 4    # increment the pointer to c

  addi a3, a3, 4    # increment the pointer to dst


  # Check if the loop should continue

  blt a5, a4, loop  # if a5 < a4, then branch to loop


# Return from the function

jr ra              # jump to the return address

This code assumes that the RISC-V ABI is being used, and that the function takes four arguments: a, b, c, and dst, which are pointers to arrays of float values, and a fifth argument size, which is the number of elements in the arrays. It also assumes that the fmadd.s instruction is available, which performs a floating point multiply-add operation in a single cycle.

The code sets up a loop that iterates over the elements of the arrays, loads the operands from the arrays using the flw.s instruction, performs the multiplication and addition using the fmul.s and fadd.s instructions, and stores the result back into the dst array using the fsw.s instruction. The loop counter and the pointers to the arrays are incremented at the end of each iteration. The loop continues until the loop counter reaches the value of size.

Finally, the code returns from the function using the jr instruction, which jumps to the return address stored in the ra register.




Abel:


Would you actually replace the fmul and fadd pair of instructions in that program with fmadd.s?


ChatGPT:


Yes, it is generally recommended to use the fmadd.s instruction whenever possible, as it can perform a floating point multiply-add operation in a single cycle, which is faster than performing the multiplication and addition separately.

In the RISC-V assembly code that I provided, the fmul.s and fadd.s instructions can be replaced with a single fmadd.s instruction as follows:

  # Load the operands

  flw.s f0, 0(a0)   # load a[i] into f0

  flw.s f1, 0(a1)   # load b[i] into f1

  flw.s f2, 0(a2)   # load c[i] into f2


  # Perform the floating point multiply-add

  fmadd.s f4, f1, f2, f0  # compute a[i] + b[i]*c[i] and store the result in f4

This reduces the number of instructions needed to perform the operation, which can improve the performance of the program.

It is important to note that the fmadd.s instruction is not available on all RISC-V processors, so it is important to check whether the instruction is supported before using it. If the fmadd.s instruction is not available, then it may be necessary to use the fmul.s and fadd.s instructions instead.




Abel:


Now, show me the whole program for translating the madd function, and not only the inner instructions of the loop.


ChatGPT:


Here is the complete RISC-V assembly code for the madd function:

# RISC-V assembly for the madd function


# Load the arguments

la a0, a           # load the address of a into a0

la a1, b           # load the address of b into a1

la a2, c           # load the address of c into a2

la a3, dst         # load the address of dst into a3

li a4, size        # load the value of size into a4


# Set up the loop

addi a5, zero, 0   # initialise the loop counter to 0


loop:

  # Load the operands

  flw.s f0, 0(a0)   # load a[i] into f0

  flw.s f1, 0(a1)   # load b[i] into f1

  flw.s f2, 0(a2)   # load c[i] into f2


  # Perform the floating point multiply-add

  fmadd.s f4, f1, f2, f0  # compute a[i] + b[i]*c[i] and store the result in f4


  # Store the result

  fsw.s f4, 0(a3)   # store the result in dst[i]


  # Increment the loop counter and the pointers

  addi a5, a5, 1    # increment the loop counter

  addi a0, a0, 4    # increment the pointer to a

  addi a1, a1, 4    # increment the pointer to b

  addi a2, a2, 4    # increment the pointer to c

  addi a3, a3, 4    # increment the pointer to dst


  # Check if the loop should continue

  blt a5, a4, loop  # if a5 < a4, then branch to loop


# Return from the function

jr ra              # jump to the return address

This code defines a function called madd that takes four arguments: a, b, c, and dst, which are pointers to arrays of float values, and a fifth argument size, which is the number of elements in the arrays. The function performs a loop that iterates over the elements of the arrays, loads the operands from the arrays using the flw.s instruction, performs the multiply-add operation using the fmadd.s instruction, and stores the result back into the dst array using the fsw.s instruction. The loop counter and the pointers to the arrays are incremented at the end of each iteration. The loop continues until the loop counter reaches the value of size.

Finally, the code returns from the function using the jr instruction, which jumps to the return address stored in the ra register.




Abel:


Can you generate a smaller  number of instructions in the inner loop by applying the following transformation?


Use the final value of a0 as a way of detecting the end of loop condition, instead of maintaining an iteration index on a5. That should make the inner loop a bit shorter.


ChatGPT:


Yes, it is possible to modify the inner loop of the madd function to use the final value of a0 as a way of detecting the end of the loop, rather than maintaining an iteration index in the a5 register. Here is the modified RISC-V assembly code for the madd function:


# RISC-V assembly for the madd function


# Load the arguments

la a0, a           # load the address of a into a0

la a1, b           # load the address of b into a1

la a2, c           # load the address of c into a2

la a3, dst         # load the address of dst into a3

li a4, size        # load the value of size into a4


# Set up the loop

addi a5, a0, 4*size  # initialise the loop counter to the final value of a0


loop:

  # Load the operands

  flw.s f0, 0(a0)   # load a[i] into f0

  flw.s f1, 0(a1)   # load b[i] into f1

  flw.s f2, 0(a2)   # load c[i] into f2


  # Perform the floating point multiply-add

  fmadd.s f4, f1, f2, f0  # compute a[i] + b[i]*c[i] and store the result in f4


  # Store the result

  fsw.s f4, 0(a3)   # store the result in dst[i]


  # Increment the pointers

  addi a0, a0, 4    # increment the pointer to a

  addi a1, a1, 4    # increment the pointer to b

  addi a2, a2, 4    # increment the pointer to c

  addi a3, a3, 4    # increment the pointer to dst


  # Check if the loop should continue

  blt a0, a5, loop  # if a0 < a5, then branch to loop


# Return from the function

jr ra              # jump to the return address

In this modified version of the code, the loop counter is initialised to the final value of a0, which is the address of the last element in the a array. The loop continues until a0 reaches this final value, at which point the loop condition is no longer satisfied and the loop terminates. This eliminates the need to maintain a separate loop counter and reduces the number of instructions in the inner loop.

It is important to note that this modified version of the code still assumes that the RISC-V ABI is being used, and that the function takes four arguments: a, b, c, and dst, which are pointers to arrays of float values, and a fifth argument size, which is the number of elements in the arrays. It also assumes that the fmadd.s instruction is available, which performs a floating point multiply-add operation in a single cycle.




Overall, I am quite impressed and looking forward to seeing how this technology allows engineers like me to approach new challenges.


20200222

Dockerized mesa 20.0 graphics library development

I often like to stay with the Ubuntu LTS releases until there is an impending need to install a newer version of a package that is not available for my Ubuntu distro.

Recently I saw myself having to build the mesa 20.0 library (the latest release as of today that was released just last month) on my Ubuntu 18.04 LTS development machine. After a quick try it became evident that most of the packages I needed, unsurprisingly, were not available.

I told to myself that maybe it was about time to upgrade to the latest Ubuntu, but something in the back of my mind also reminded me that maybe it was also about time to give a try to this thing called Docker that I had for long been purportedly avoiding because it sounded too much like DevOps technology.

Let me tell you what happened saving you the gory details: I did manage to build my own docker image for building mesa. I would not say it was a fun task that took no time at all, it was tedious, but it was tedious work that now can be reused by other people, or even myself in the future.

The realization of how reusable my mesa 20.0 development environment is now was for me like an enlightening flash of light. Please share your comments on the subject here.

20180114

¿Qué es una criptomoneda?

Un sistema de pago tiene que garantizar tres propiedades básicas:

1) Una moneda solo debe pertenecer a una persona.

2) La persona a la que pertenece una moneda debe dar su consentimiento para que la moneda pase a pertenecer a otra persona y elegir libremente a quien entregar su moneda.

3) Una vez que un pago ha sido efectuado, este es irreversible y el pagador no puede repudiar la operación.

Además de estas tres propiedades básicas, hay otras dos propiedades opcionales que los usuarios de monedas pueden requerir: privacidad y fungibilidad. Hablaremos de estas dos propiedades opcionales más adelante.

Cuando usamos un banco para pagar y cobrar dinero lo único que nos hace pensar que las propiedades del sistema de pago (o reglas de juego) se cumplen es nuestra confianza en el buen hacer de todos los elementos del sistema bancario. La realidad es que a veces basta con que un solo elemento de la cadena de pago sea corrupto para que un sistema de pago no funcione.

Por ejemplo, si pagamos a un proveedor a través de nuestro banco y todo parace correcto para nosotros y nuestro banco, pero en el banco del destinatario del dinero lo ingresan a una persona “amiga” del banco distinta de la persona a la queremos pagar. La propiedad 2) no se cumple porque no hemos podido elegir a quien pagábamos.

Una criptomoneda es un sistema de pago electrónico descentralizado donde las tres propiedades básicas mencionadas arriba están garantizadas mediante técnicas criptográficas en vez de requerir la confiabilidad de todos los elementos de la cadena de pago.

La palabra criptomoneda es una traducción literal de la palabra inglesa “cryptocurrency”, que podría definirse como una moneda electrónica donde la confianza ha sido sustituida por técnicas criptográficas para evitar la necesidad de confiar.


20160109

Votar en sentido distinto

Me quedo a cuadros leyendo algunos de los detalles del acuerdo entre la CUP y Junts pel Sí en El País:
“...la CUP cederá dos de sus diez diputados para que trabajen dentro del grupo el grupo de Junts pel Sí. Los otros diputados se comprometen a no votar nunca en el mismo sentido que los partidos contrarios al derecho a decidir...”
O sea, que podría llegar el portavoz del PP con una bolsa llena de pastillas y decir:

- Voy a pasar por todos los sillones de la cámara con una bolsa de pastillas. No son Juanolas, sino cianuro. El que esté a favor de envenenarse a sí mismo que coja una y se la trague. El que no esté de acuerdo con la propuesta de envenarse que no la coja. Os adelanto que el PP votará no envenenarse.

Y podríamos tener una desgracia.

20150527

Las niñas perdidas

Leo hoy un artículo en El País que me recuerda discusiones que tenía un poco olvidadas y que me valieron perder alguna vieja amistad en el Facebook. Se trata de un artículo acerca de los abortos selectivos de niñas.


Después de haber quemado hasta la saciedad el slogan de "nosotras parimos nosotras decidimos" la izquierda solo podría autocontradecirse si se pronuncia contra los abortos selectivos de niñas.

¿Niñas perdidas? ¿Qué niñas? Si los no nacidos se consideraban parásitos en todas las discusiones de barra de bar que ya tuvimos cuando Gallardón quería poner límites a las libertades de las embarazadas...

Por no hablar de la posibilidad de que en el futuro podamos determinar con una prueba genética tendencias de orientación sexual. ¿Qué pasará cuando libertad de las embarazadas para abortar se use para discriminar a los gays antes de que puedan nacer?

¿Gays perdidos? ¿Qué personas gays? Si los no nacidos se consideraban parásitos en vez de personas... Tarde o temprano volverán las oscuras contradicciones, pero los no nacidos, esos ya no volverán.

20150327

El papel del estado en las políticas energéticas

Creo que todo lo que el estado puede hacer en tema de política energética o bien es superfluo o viola nuestra libertades. Tres funciones que solemos atribuir al estado son:

1) Asegurar la moralidad de la actividad económica.
2) Obligar a consumir lo correcto.
3) Desviar recursos a la investigación.

En el primer caso, los aspectos morales de la actividad económica se pueden reflejar en los precios. Si una empresa respeta el medio ambiente y otra no, entonces los clientes a los que eso les importe estarán dispuestos a pagar más por los productos de la empresa que hace lo correcto. Si a los clientes no les importa si se respeta el medio ambiente, entonces tampoco verán ninguna necesidad de que el estado obligue a las empresas a respetar el medio ambiente.

En el segundo caso, si los ciudadanos quisieran políticas que les obligasen a comprar coches eléctricos, no necesitarían elegir a un gobierno que les impusiera la elección como una política de obligado cumplimiento, sino que directamente podrían comprar el coche que consideran correcto. Si por el contrario los ciudadanos no quisieran comprar coches eléctricos tampoco querrían un estado tiránico que les impusiera una política.

Por último, sobre el papel del estado en la financiación de la ciencia. Lo primero decir que la ciencia que tiene aplicación directa la puede hacer la empresa privada, dejándonos sólo con el problema de cómo financiar la ciencia que no tenga aplicación directa.

El razonamiento de que necesitamos un estado para que desvíe recursos a la investigación no me sirve. Estaréis de acuerdo en que los que financian la investigación son los ciudadanos y el estado es un intermediario. Por ejemplo, en España, ¿quién financia la investigación los 350 miembros del parlamento con un sueldo medio de 75.000 euros al año o los 46 millones de ciudadanos españoles con un sueldo medio de 26.000 euros? Está claro, ¿no?

Como al final los que pagan son los ciudadanos lo que necesita la ciencia es negociar sin intermediarios a través del crowdfunding. El estado no sólo no se necesita como intermediario sino que además es un intermediario muy caro.

20150322

Un Silicon Valley espanyol

A menudo escucho a gente en Espanya discutir sobre la posibilidad de que crear un Silicon Valley espanyol por medio de la intervencion del estado. Podemos crear tejido industrial a base de crear parques tecnologicos y subvencionar startups? En este breve post resumo mi punto de vista.

Creo que lo que ha pasado en el Silicon Valley es una combinacion de factores:

- Forma parte de un pais relativamente nuevo, donde emigro gente con iniciativa desde la vieja Europa. Ha sido durante mucho tiempo una tierra de oportunidades donde contaba mas tu habilidad para trabajar que la posicion de tu familia en la jerarquia social.

- Sus universidades se crearon como consecuencia de la revolucion industrial, priorizando temas con interes economico.

- A pesar de ser relativamente nuevas, esas universidades no han formado su personal de la noche a la manyana (en 20 o 30 anyos), y no han crecido de la nada con inyecciones de dinero publico. En este sentido las universidades posiblemente tienen una edad optima y un crecimiento natural, con una intervencion del estado minima o nula.

- La educacion publica no desperdicia recursos en ensenyar y promover lenguas locales.

- En USA el tener un lenguaje comun en todo el territorio facilita el que la gente se mueva. Se nace en un sitio, se estudia en otro, se trabaja en otro y se espera lo mismo de la siguiente generacion.

- Probablemente los politicos californianos no han hecho nada, aparte de mantener las carreteras. Y casualmente en la carreteras del Silicon Valley yo he visto socavones mas grandes que los que me encontre cuando fui a Grecia.

- El hecho de que los ciudadanos americanos eligiesen California para su Silicon Valley y no Tejas se debe a motivos aleatorios. La industria tiende a concentrarse por motivos que al principio son aleatorios e incontrolables. Con el paso del tiempo la mano de obra va donde esta el tejido industrial y el tejido industrial crece donde esta la mano obra.

Lo anterior aplicado al caso espanyol se resume en:

- Hay una componente de actitud positiva que se necesita para atraer al talento a Espanya. No podemos atraer talento si el pais no se forja una reputacion de tierra de oportunidades para la gente con talento.

- Las universidades tienen que madurar e idealmente deberian llegar a ser autosuficientes.

- Necesitamos mas movilidad, probablemente a nivel europeo.

- Los idiomas locales no ayudan en nada.

- Los politicos no deben hacer mas, sino menos.

- Si todo lo anterior mejora, los espanyoles tendran un Silicon Valley y su ubicacion se elegira por motivos fuera de alcance de ninguna politica.